/* * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.internal.configuration.injection; import org.mockito.exceptions.Reporter; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.configuration.injection.filter.FinalMockCandidateFilter; import org.mockito.internal.configuration.injection.filter.MockCandidateFilter; import org.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter; import org.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter; import org.mockito.internal.util.collections.ListUtil; import org.mockito.internal.util.reflection.FieldInitializationReport; import org.mockito.internal.util.reflection.FieldInitializer; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.*; import static org.mockito.internal.util.collections.Sets.newMockSafeHashSet; /** * Inject mocks using first setters then fields, if no setters available. * *

* Algorithm :
* for each field annotated by @InjectMocks *

*

* *

* Note: If the field needing injection is not initialized, the strategy tries * to create one using a no-arg constructor of the field type. *

*/ public class PropertyAndSetterInjection extends MockInjectionStrategy { private final MockCandidateFilter mockCandidateFilter = new TypeBasedCandidateFilter(new NameBasedCandidateFilter(new FinalMockCandidateFilter())); private Comparator superTypesLast = new FieldTypeAndNameComparator(); private ListUtil.Filter notFinalOrStatic = new ListUtil.Filter() { public boolean isOut(Field object) { return Modifier.isFinal(object.getModifiers()) || Modifier.isStatic(object.getModifiers()); } }; public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set mockCandidates) { // Set mocksToBeInjected = new HashSet(mockCandidates); FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner); // for each field in the class hierarchy boolean injectionOccurred = false; Class fieldClass = report.fieldClass(); Object fieldInstanceNeedingInjection = report.fieldInstance(); while (fieldClass != Object.class) { injectionOccurred |= injectMockCandidates(fieldClass, newMockSafeHashSet(mockCandidates), fieldInstanceNeedingInjection); fieldClass = fieldClass.getSuperclass(); } return injectionOccurred; } private FieldInitializationReport initializeInjectMocksField(Field field, Object fieldOwner) { FieldInitializationReport report = null; try { report = new FieldInitializer(fieldOwner, field).initialize(); } catch (MockitoException e) { if(e.getCause() instanceof InvocationTargetException) { Throwable realCause = e.getCause().getCause(); new Reporter().fieldInitialisationThrewException(field, realCause); } new Reporter().cannotInitializeForInjectMocksAnnotation(field.getName(), e); } return report; // never null } private boolean injectMockCandidates(Class awaitingInjectionClazz, Set mocks, Object instance) { boolean injectionOccurred = false; List orderedInstanceFields = orderedInstanceFieldsFrom(awaitingInjectionClazz); // pass 1 injectionOccurred |= injectMockCandidatesOnFields(mocks, instance, injectionOccurred, orderedInstanceFields); // pass 2 injectionOccurred |= injectMockCandidatesOnFields(mocks, instance, injectionOccurred, orderedInstanceFields); return injectionOccurred; } private boolean injectMockCandidatesOnFields(Set mocks, Object instance, boolean injectionOccurred, List orderedInstanceFields) { for (Iterator it = orderedInstanceFields.iterator(); it.hasNext(); ) { Field field = it.next(); Object injected = mockCandidateFilter.filterCandidate(mocks, field, instance).thenInject(); if (injected != null) { injectionOccurred |= true; mocks.remove(injected); it.remove(); } } return injectionOccurred; } private List orderedInstanceFieldsFrom(Class awaitingInjectionClazz) { List declaredFields = Arrays.asList(awaitingInjectionClazz.getDeclaredFields()); declaredFields = ListUtil.filter(declaredFields, notFinalOrStatic); Collections.sort(declaredFields, superTypesLast); return declaredFields; } static class FieldTypeAndNameComparator implements Comparator { public int compare(Field field1, Field field2) { Class field1Type = field1.getType(); Class field2Type = field2.getType(); // if same type, compares on field name if (field1Type == field2Type) { return field1.getName().compareTo(field2.getName()); } if(field1Type.isAssignableFrom(field2Type)) { return 1; } if(field2Type.isAssignableFrom(field1Type)) { return -1; } return 0; } } }