mockit.internal.injection.Injector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external
APIs; JUnit (4 & 5) and TestNG test runners are supported.
It also contains an advanced code coverage tool.
The newest version!
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.injection;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isStatic;
import static java.lang.reflect.Modifier.isVolatile;
import static java.util.regex.Pattern.compile;
import static mockit.internal.injection.InjectionPoint.PERSISTENCE_UNIT_CLASS;
import static mockit.internal.injection.InjectionPoint.convertToLegalJavaIdentifierIfNeeded;
import static mockit.internal.injection.InjectionPoint.getQualifiedName;
import static mockit.internal.injection.InjectionPoint.isServlet;
import static mockit.internal.injection.InjectionPoint.kindOfInjectionPoint;
import static mockit.internal.injection.InjectionPoint.wrapInProviderIfNeeded;
import static mockit.internal.injection.InjectionProvider.NULL;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.persistence.Entity;
import mockit.internal.injection.InjectionPoint.KindOfInjectionPoint;
import mockit.internal.injection.field.FieldToInject;
import mockit.internal.injection.full.FullInjection;
import mockit.internal.reflection.FieldReflection;
import mockit.internal.util.DefaultValues;
public class Injector {
private static final Pattern TYPE_NAME = compile("class |interface |java\\.lang\\.");
@NonNull
protected final InjectionState injectionState;
@Nullable
protected final FullInjection fullInjection;
protected Injector(@NonNull InjectionState state, @Nullable FullInjection fullInjection) {
injectionState = state;
this.fullInjection = fullInjection;
}
@NonNull
static List findAllTargetInstanceFieldsInTestedClassHierarchy(@NonNull Class> actualTestedClass,
@NonNull TestedClass testedClass) {
List targetFields = new ArrayList<>();
Class> classWithFields = actualTestedClass;
do {
addEligibleFields(targetFields, classWithFields);
classWithFields = classWithFields.getSuperclass();
} while (testedClass.isClassFromSameModuleOrSystemAsTestedClass(classWithFields) || isServlet(classWithFields));
return targetFields;
}
private static void addEligibleFields(@NonNull List targetFields, @NonNull Class> classWithFields) {
Field[] fields = classWithFields.getDeclaredFields();
for (Field field : fields) {
if (isEligibleForInjection(field)) {
targetFields.add(field);
}
}
}
private static boolean isEligibleForInjection(@NonNull Field field) {
int modifiers = field.getModifiers();
if (isFinal(modifiers)) {
return false;
}
if (kindOfInjectionPoint(field) != KindOfInjectionPoint.NotAnnotated) {
return true;
}
// noinspection SimplifiableIfStatement
if (PERSISTENCE_UNIT_CLASS != null && field.getType().isAnnotationPresent(Entity.class)) {
return false;
}
return !isStatic(modifiers) && !isVolatile(modifiers);
}
public final void fillOutDependenciesRecursively(@NonNull Object dependency, @NonNull TestedClass testedClass) {
Class> dependencyClass = dependency.getClass();
List targetFields = findAllTargetInstanceFieldsInTestedClassHierarchy(dependencyClass, testedClass);
if (!targetFields.isEmpty()) {
List currentlyConsumedInjectables = injectionState.injectionProviders
.saveConsumedInjectionProviders();
injectIntoEligibleFields(targetFields, dependency, testedClass);
injectionState.injectionProviders.restoreConsumedInjectionProviders(currentlyConsumedInjectables);
}
}
public final void injectIntoEligibleFields(@NonNull List targetFields, @NonNull Object testedObject,
@NonNull TestedClass testedClass) {
for (Field field : targetFields) {
if (targetFieldWasNotAssignedByConstructor(testedObject, field)) {
Object injectableValue = getValueForFieldIfAvailable(targetFields, testedClass, field);
if (injectableValue != null && injectableValue != NULL) {
injectableValue = wrapInProviderIfNeeded(field.getGenericType(), injectableValue);
FieldReflection.setFieldValue(field, testedObject, injectableValue);
}
}
}
}
private static boolean targetFieldWasNotAssignedByConstructor(@NonNull Object testedObject,
@NonNull Field targetField) {
if (kindOfInjectionPoint(targetField) != KindOfInjectionPoint.NotAnnotated) {
return true;
}
Object fieldValue = FieldReflection.getFieldValue(targetField, testedObject);
if (fieldValue == null) {
return true;
}
Class> fieldType = targetField.getType();
if (!fieldType.isPrimitive()) {
return false;
}
Object defaultValue = DefaultValues.defaultValueForPrimitiveType(fieldType);
return fieldValue.equals(defaultValue);
}
@Nullable
private Object getValueForFieldIfAvailable(@NonNull List targetFields, @NonNull TestedClass testedClass,
@NonNull Field targetField) {
@Nullable
String qualifiedFieldName = getQualifiedName(targetField.getDeclaredAnnotations());
InjectionProvider injectable = findAvailableInjectableIfAny(targetFields, qualifiedFieldName, testedClass,
targetField);
if (injectable != null) {
return injectionState.getValueToInject(injectable);
}
InjectionProvider fieldToInject = new FieldToInject(targetField);
Type typeToInject = fieldToInject.getDeclaredType();
InjectionPoint injectionPoint = new InjectionPoint(typeToInject, fieldToInject.getName(), qualifiedFieldName);
TestedClass nextTestedClass = typeToInject instanceof TypeVariable> ? testedClass
: new TestedClass(typeToInject, fieldToInject.getClassOfDeclaredType(), testedClass);
Object testedValue = injectionState.getTestedValue(nextTestedClass, injectionPoint);
if (testedValue != null) {
return testedValue;
}
if (fullInjection != null) {
Object newInstance = fullInjection.createOrReuseInstance(nextTestedClass, this, fieldToInject,
qualifiedFieldName);
if (newInstance != null) {
return newInstance;
}
}
KindOfInjectionPoint kindOfInjectionPoint = kindOfInjectionPoint(targetField);
throwExceptionIfUnableToInjectRequiredTargetField(kindOfInjectionPoint, targetField, qualifiedFieldName);
return null;
}
@Nullable
private InjectionProvider findAvailableInjectableIfAny(@NonNull List targetFields,
@Nullable String qualifiedTargetFieldName, @NonNull TestedClass testedClass, @NonNull Field targetField) {
KindOfInjectionPoint kindOfInjectionPoint = kindOfInjectionPoint(targetField);
InjectionProviders injectionProviders = injectionState.injectionProviders;
injectionProviders.setTypeOfInjectionPoint(targetField.getGenericType(), kindOfInjectionPoint);
if (qualifiedTargetFieldName != null && !qualifiedTargetFieldName.isEmpty()) {
String injectableName = convertToLegalJavaIdentifierIfNeeded(qualifiedTargetFieldName);
InjectionProvider matchingInjectable = injectionProviders.findInjectableByTypeAndName(injectableName,
testedClass);
if (matchingInjectable != null) {
return matchingInjectable;
}
}
String targetFieldName = targetField.getName();
if (withMultipleTargetFieldsOfSameType(targetFields, testedClass, targetField, injectionProviders)) {
return injectionProviders.findInjectableByTypeAndName(targetFieldName, testedClass);
}
return injectionProviders.getProviderByTypeAndOptionallyName(targetFieldName, testedClass);
}
private static boolean withMultipleTargetFieldsOfSameType(@NonNull List targetFields,
@NonNull TestedClass testedClass, @NonNull Field targetField,
@NonNull InjectionProviders injectionProviders) {
for (Field anotherTargetField : targetFields) {
if (anotherTargetField != targetField && injectionProviders
.isAssignableToInjectionPoint(anotherTargetField.getGenericType(), testedClass)) {
return true;
}
}
return false;
}
private void throwExceptionIfUnableToInjectRequiredTargetField(@NonNull KindOfInjectionPoint kindOfInjectionPoint,
@NonNull Field targetField, @Nullable String qualifiedFieldName) {
if (kindOfInjectionPoint == KindOfInjectionPoint.Required) {
Type fieldType = targetField.getGenericType();
String fieldTypeName = fieldType.toString();
fieldTypeName = TYPE_NAME.matcher(fieldTypeName).replaceAll("");
String kindOfInjectable = "@Tested or @Injectable";
if (fullInjection != null) {
if (targetField.getType().isInterface()) {
kindOfInjectable = "@Tested instance of an implementation class";
} else {
kindOfInjectable = "@Tested object";
}
}
throw new IllegalStateException("Missing " + kindOfInjectable + " for field " + fieldTypeName + ' '
+ targetField.getName() + (qualifiedFieldName == null ? "" : " (\"" + qualifiedFieldName + "\")")
+ " in " + targetField.getDeclaringClass().getSimpleName());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy