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.injection.scanner;
6
7import org.mockito.Mock;
8import org.mockito.MockitoAnnotations;
9import org.mockito.Spy;
10import org.mockito.internal.util.MockUtil;
11import org.mockito.internal.util.reflection.FieldReader;
12
13import java.lang.reflect.Field;
14import java.util.Set;
15
16import static org.mockito.internal.util.collections.Sets.newMockSafeHashSet;
17
18/**
19 * Scan mocks, and prepare them if needed.
20 */
21public class MockScanner {
22    private MockUtil mockUtil = new MockUtil();
23    private final Object instance;
24    private final Class<?> clazz;
25
26    /**
27     * Creates a MockScanner.
28     *
29     * @param instance The test instance
30     * @param clazz    The class in the type hierarchy of this instance.
31     */
32    public MockScanner(Object instance, Class<?> clazz) {
33        this.instance = instance;
34        this.clazz = clazz;
35    }
36
37    /**
38     * Add the scanned and prepared mock instance to the given collection.
39     *
40     * <p>
41     * The preparation of mocks consists only in defining a MockName if not already set.
42     * </p>
43     *
44     * @param mocks Set of mocks
45     */
46    public void addPreparedMocks(Set<Object> mocks) {
47        mocks.addAll(scan());
48    }
49
50    /**
51     * Scan and prepare mocks for the given <code>testClassInstance</code> and <code>clazz</code> in the type hierarchy.
52     *
53     * @return A prepared set of mock
54     */
55    private Set<Object> scan() {
56        Set<Object> mocks = newMockSafeHashSet();
57        for (Field field : clazz.getDeclaredFields()) {
58            // mock or spies only
59            FieldReader fieldReader = new FieldReader(instance, field);
60
61            Object mockInstance = preparedMock(fieldReader.read(), field);
62            if (mockInstance != null) {
63                mocks.add(mockInstance);
64            }
65        }
66        return mocks;
67    }
68
69    private Object preparedMock(Object instance, Field field) {
70        if (isAnnotatedByMockOrSpy(field)) {
71            return instance;
72        } else if (isMockOrSpy(instance)) {
73            mockUtil.maybeRedefineMockName(instance, field.getName());
74            return instance;
75        }
76        return null;
77    }
78
79    private boolean isAnnotatedByMockOrSpy(Field field) {
80        return null != field.getAnnotation(Spy.class)
81                || null != field.getAnnotation(Mock.class)
82                || null != field.getAnnotation(MockitoAnnotations.Mock.class);
83    }
84
85    private boolean isMockOrSpy(Object instance) {
86        return mockUtil.isMock(instance)
87                || mockUtil.isSpy(instance);
88    }
89}
90