1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5package org.mockito.internal.configuration;
6
7import java.lang.reflect.Field;
8import java.util.HashSet;
9import java.util.Set;
10import org.mockito.MockitoAnnotations;
11import org.mockito.internal.configuration.injection.scanner.InjectMocksScanner;
12import org.mockito.internal.configuration.injection.scanner.MockScanner;
13import org.mockito.plugins.AnnotationEngine;
14
15import static org.mockito.internal.util.collections.Sets.newMockSafeHashSet;
16
17/**
18 * See {@link MockitoAnnotations}
19 */
20public class InjectingAnnotationEngine implements AnnotationEngine, org.mockito.configuration.AnnotationEngine {
21    private final AnnotationEngine delegate = new IndependentAnnotationEngine();
22    private final AnnotationEngine spyAnnotationEngine = new SpyAnnotationEngine();
23
24    /**
25     * Process the fields of the test instance and create Mocks, Spies, Captors and inject them on fields
26     * annotated @InjectMocks.
27     *
28     * <p>
29     * This code process the test class and the super classes.
30     * <ol>
31     * <li>First create Mocks, Spies, Captors.</li>
32     * <li>Then try to inject them.</li>
33     * </ol>
34     *
35     * @param clazz Not used
36     * @param testInstance The instance of the test, should not be null.
37     *
38     * @see org.mockito.plugins.AnnotationEngine#process(Class, Object)
39     */
40    public void process(Class<?> clazz, Object testInstance) {
41        processIndependentAnnotations(testInstance.getClass(), testInstance);
42        processInjectMocks(testInstance.getClass(), testInstance);
43    }
44
45    private void processInjectMocks(final Class<?> clazz, final Object testInstance) {
46        Class<?> classContext = clazz;
47        while (classContext != Object.class) {
48            injectMocks(testInstance);
49            classContext = classContext.getSuperclass();
50        }
51    }
52
53    private void processIndependentAnnotations(final Class<?> clazz, final Object testInstance) {
54        Class<?> classContext = clazz;
55        while (classContext != Object.class) {
56            //this will create @Mocks, @Captors, etc:
57            delegate.process(classContext, testInstance);
58            //this will create @Spies:
59            spyAnnotationEngine.process(classContext, testInstance);
60
61            classContext = classContext.getSuperclass();
62        }
63    }
64
65
66    /**
67     * Initializes mock/spies dependencies for objects annotated with
68     * &#064;InjectMocks for given testClassInstance.
69     * <p>
70     * See examples in javadoc for {@link MockitoAnnotations} class.
71     *
72     * @param testClassInstance
73     *            Test class, usually <code>this</code>
74     */
75    public void injectMocks(final Object testClassInstance) {
76        Class<?> clazz = testClassInstance.getClass();
77        Set<Field> mockDependentFields = new HashSet<Field>();
78        Set<Object> mocks = newMockSafeHashSet();
79
80        while (clazz != Object.class) {
81            new InjectMocksScanner(clazz).addTo(mockDependentFields);
82            new MockScanner(testClassInstance, clazz).addPreparedMocks(mocks);
83            onInjection(testClassInstance, clazz, mockDependentFields, mocks);
84            clazz = clazz.getSuperclass();
85        }
86
87        new DefaultInjectionEngine().injectMocksOnFields(mockDependentFields, mocks, testClassInstance);
88    }
89
90    protected void onInjection(Object testClassInstance, Class<?> clazz, Set<Field> mockDependentFields, Set<Object> mocks) {
91
92    }
93
94}
95