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 org.mockito.Captor;
8import org.mockito.Mock;
9import org.mockito.MockitoAnnotations;
10import org.mockito.configuration.AnnotationEngine;
11import org.mockito.exceptions.Reporter;
12import org.mockito.exceptions.base.MockitoException;
13import org.mockito.internal.util.reflection.FieldSetter;
14
15import java.lang.annotation.Annotation;
16import java.lang.reflect.Field;
17import java.util.HashMap;
18import java.util.Map;
19
20/**
21 * Initializes fields annotated with @{@link org.mockito.Mock} or @{@link org.mockito.Captor}.
22 *
23 * <p>
24 * The {@link #process(Class, Object)} method implementation <strong>does not</strong> process super classes!
25 *
26 * @see MockitoAnnotations
27 */
28@SuppressWarnings("unchecked")
29public class DefaultAnnotationEngine implements AnnotationEngine {
30    private final Map<Class<? extends Annotation>, FieldAnnotationProcessor<?>> annotationProcessorMap = new HashMap<Class<? extends Annotation>, FieldAnnotationProcessor<?>>();
31
32    public DefaultAnnotationEngine() {
33        registerAnnotationProcessor(Mock.class, new MockAnnotationProcessor());
34        registerAnnotationProcessor(MockitoAnnotations.Mock.class, new MockitoAnnotationsMockAnnotationProcessor());
35        registerAnnotationProcessor(Captor.class, new CaptorAnnotationProcessor());
36    }
37
38    /* (non-Javadoc)
39    * @see org.mockito.AnnotationEngine#createMockFor(java.lang.annotation.Annotation, java.lang.reflect.Field)
40    */
41    @SuppressWarnings("deprecation")
42    public Object createMockFor(Annotation annotation, Field field) {
43        return forAnnotation(annotation).process(annotation, field);
44    }
45
46    private <A extends Annotation> FieldAnnotationProcessor<A> forAnnotation(A annotation) {
47        if (annotationProcessorMap.containsKey(annotation.annotationType())) {
48            return (FieldAnnotationProcessor<A>) annotationProcessorMap.get(annotation.annotationType());
49        }
50        return new FieldAnnotationProcessor<A>() {
51            public Object process(A annotation, Field field) {
52                return null;
53            }
54        };
55    }
56
57    private <A extends Annotation> void registerAnnotationProcessor(Class<A> annotationClass, FieldAnnotationProcessor<A> fieldAnnotationProcessor) {
58        annotationProcessorMap.put(annotationClass, fieldAnnotationProcessor);
59    }
60
61    public void process(Class<?> clazz, Object testInstance) {
62        Field[] fields = clazz.getDeclaredFields();
63        for (Field field : fields) {
64            boolean alreadyAssigned = false;
65            for(Annotation annotation : field.getAnnotations()) {
66                Object mock = createMockFor(annotation, field);
67                if (mock != null) {
68                    throwIfAlreadyAssigned(field, alreadyAssigned);
69                    alreadyAssigned = true;
70                    try {
71                        new FieldSetter(testInstance, field).set(mock);
72                    } catch (Exception e) {
73                        throw new MockitoException("Problems setting field " + field.getName() + " annotated with "
74                                + annotation, e);
75                    }
76                }
77            }
78        }
79    }
80
81    void throwIfAlreadyAssigned(Field field, boolean alreadyAssigned) {
82        if (alreadyAssigned) {
83            new Reporter().moreThanOneAnnotationNotAllowed(field.getName());
84        }
85    }
86
87}