1e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson/*
2e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Copyright (c) 2007 Mockito contributors
3e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * This program is made available under the terms of the MIT License.
4e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */
5e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
6e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonpackage org.mockito.internal.configuration.injection;
7e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
8e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.exceptions.Reporter;
9e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.exceptions.base.MockitoException;
10e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.configuration.injection.filter.FinalMockCandidateFilter;
11e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.configuration.injection.filter.MockCandidateFilter;
12e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter;
13e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter;
14e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.util.collections.ListUtil;
15e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.util.reflection.FieldInitializationReport;
16e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.internal.util.reflection.FieldInitializer;
17e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
18e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.lang.reflect.Field;
19e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.lang.reflect.InvocationTargetException;
20e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.lang.reflect.Modifier;
21e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.util.*;
22e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
23e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport static org.mockito.internal.util.collections.Sets.newMockSafeHashSet;
24e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
25e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson/**
26e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Inject mocks using first setters then fields, if no setters available.
27e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *
28e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <p>
29e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <u>Algorithm :<br></u>
30e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * for each field annotated by @InjectMocks
31e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *   <ul>
32e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *   <li>initialize field annotated by @InjectMocks
33e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *   <li>for each fields of a class in @InjectMocks type hierarchy
34e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     <ul>
35e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     <li>make a copy of mock candidates
36e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     <li>order fields rom sub-type to super-type, then by field name
37e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     <li>for the list of fields in a class try two passes of :
38e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *         <ul>
39e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *             <li>find mock candidate by type
40e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *             <li>if more than <b>*one*</b> candidate find mock candidate on name
41e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *             <li>if one mock candidate then
42e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *                 <ul>
43e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *                     <li>set mock by property setter if possible
44e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *                     <li>else set mock by field injection
45e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *                 </ul>
46e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *             <li>remove mock from mocks copy (mocks are just injected once in a class)
47e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *             <li>remove injected field from list of class fields
48e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *         </ul>
49e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     <li>else don't fail, user will then provide dependencies
50e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *     </ul>
51e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *   </ul>
52e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </p>
53e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson *
54e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <p>
55e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <u>Note:</u> If the field needing injection is not initialized, the strategy tries
56e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * to create one using a no-arg constructor of the field type.
57e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </p>
58e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */
59e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonpublic class PropertyAndSetterInjection extends MockInjectionStrategy {
60e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
61e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private final MockCandidateFilter mockCandidateFilter = new TypeBasedCandidateFilter(new NameBasedCandidateFilter(new FinalMockCandidateFilter()));
62e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private Comparator<Field> superTypesLast = new FieldTypeAndNameComparator();
63e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
64e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private ListUtil.Filter<Field> notFinalOrStatic = new ListUtil.Filter<Field>() {
65e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        public boolean isOut(Field object) {
66e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            return Modifier.isFinal(object.getModifiers()) || Modifier.isStatic(object.getModifiers());
67e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        }
68e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    };
69e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
70e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
71e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates) {
72e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        // Set<Object> mocksToBeInjected = new HashSet<Object>(mockCandidates);
73e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);
74e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
75e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        // for each field in the class hierarchy
76e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        boolean injectionOccurred = false;
77e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        Class<?> fieldClass = report.fieldClass();
78e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        Object fieldInstanceNeedingInjection = report.fieldInstance();
79e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        while (fieldClass != Object.class) {
80e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            injectionOccurred |= injectMockCandidates(fieldClass, newMockSafeHashSet(mockCandidates), fieldInstanceNeedingInjection);
81e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            fieldClass = fieldClass.getSuperclass();
82e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        }
83e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        return injectionOccurred;
84e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
85e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
86e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private FieldInitializationReport initializeInjectMocksField(Field field, Object fieldOwner) {
87e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        FieldInitializationReport report = null;
88e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        try {
89e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            report = new FieldInitializer(fieldOwner, field).initialize();
90e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        } catch (MockitoException e) {
91e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            if(e.getCause() instanceof InvocationTargetException) {
92e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                Throwable realCause = e.getCause().getCause();
93e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                new Reporter().fieldInitialisationThrewException(field, realCause);
94e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            }
95e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            new Reporter().cannotInitializeForInjectMocksAnnotation(field.getName(), e);
96e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        }
97e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        return report; // never null
98e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
99e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
100e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
101e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private boolean injectMockCandidates(Class<?> awaitingInjectionClazz, Set<Object> mocks, Object instance) {
102e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        boolean injectionOccurred = false;
103e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        List<Field> orderedInstanceFields = orderedInstanceFieldsFrom(awaitingInjectionClazz);
104e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        // pass 1
105e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        injectionOccurred |= injectMockCandidatesOnFields(mocks, instance, injectionOccurred, orderedInstanceFields);
106e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        // pass 2
107e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        injectionOccurred |= injectMockCandidatesOnFields(mocks, instance, injectionOccurred, orderedInstanceFields);
108e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        return injectionOccurred;
109e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
110e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
111e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private boolean injectMockCandidatesOnFields(Set<Object> mocks, Object instance, boolean injectionOccurred, List<Field> orderedInstanceFields) {
112e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        for (Iterator<Field> it = orderedInstanceFields.iterator(); it.hasNext(); ) {
113e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            Field field = it.next();
114e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            Object injected = mockCandidateFilter.filterCandidate(mocks, field, instance).thenInject();
115e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            if (injected != null) {
116e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                injectionOccurred |= true;
117e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                mocks.remove(injected);
118e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                it.remove();
119e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            }
120e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        }
121e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        return injectionOccurred;
122e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
123e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
124e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    private List<Field> orderedInstanceFieldsFrom(Class<?> awaitingInjectionClazz) {
125e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        List<Field> declaredFields = Arrays.asList(awaitingInjectionClazz.getDeclaredFields());
126e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        declaredFields = ListUtil.filter(declaredFields, notFinalOrStatic);
127e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
128e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        Collections.sort(declaredFields, superTypesLast);
129e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
130e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        return declaredFields;
131e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
132e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
133e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    static class FieldTypeAndNameComparator implements Comparator<Field> {
134e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        public int compare(Field field1, Field field2) {
135e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            Class<?> field1Type = field1.getType();
136e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            Class<?> field2Type = field2.getType();
137e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson
138e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            // if same type, compares on field name
139e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            if (field1Type == field2Type) {
140e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                return field1.getName().compareTo(field2.getName());
141e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            }
142e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            if(field1Type.isAssignableFrom(field2Type)) {
143e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                return 1;
144e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            }
145e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            if(field2Type.isAssignableFrom(field1Type)) {
146e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson                return -1;
147e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            }
148e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson            return 0;
149e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson        }
150e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson    }
151e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson}
152