1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5
6package org.mockito.internal.configuration.injection;
7
8import org.mockito.exceptions.Reporter;
9import org.mockito.exceptions.base.MockitoException;
10import org.mockito.internal.util.reflection.FieldInitializationReport;
11import org.mockito.internal.util.reflection.FieldInitializer;
12import org.mockito.internal.util.reflection.FieldInitializer.ConstructorArgumentResolver;
13
14import java.lang.reflect.Field;
15import java.lang.reflect.InvocationTargetException;
16import java.util.ArrayList;
17import java.util.List;
18import java.util.Set;
19
20/**
21 * Injection strategy based on constructor.
22 *
23 * <p>
24 * The strategy will search for the constructor with most parameters
25 * and try to resolve mocks by type.
26 * </p>
27 *
28 * <blockquote>
29 * TODO on missing mock type, shall it abandon or create "noname" mocks.
30 * TODO and what if the arg type is not mockable.
31 * </blockquote>
32 *
33 * <p>
34 * For now the algorithm tries to create anonymous mocks if an argument type is missing.
35 * If not possible the algorithm abandon resolution.
36 * </p>
37 */
38public class ConstructorInjection extends MockInjectionStrategy {
39
40    private ConstructorArgumentResolver argResolver;
41
42    public ConstructorInjection() { }
43
44    // visible for testing
45    ConstructorInjection(ConstructorArgumentResolver argResolver) {
46        this.argResolver = argResolver;
47    }
48
49    public boolean processInjection(Field field, Object fieldOwner, Set<Object> mockCandidates) {
50        try {
51            SimpleArgumentResolver simpleArgumentResolver = new SimpleArgumentResolver(mockCandidates);
52            FieldInitializationReport report = new FieldInitializer(fieldOwner, field, simpleArgumentResolver).initialize();
53
54            return report.fieldWasInitializedUsingContructorArgs();
55        } catch (MockitoException e) {
56            if(e.getCause() instanceof InvocationTargetException) {
57                Throwable realCause = e.getCause().getCause();
58                new Reporter().fieldInitialisationThrewException(field, realCause);
59            }
60            // other causes should be fine
61            return false;
62        }
63
64    }
65
66    /**
67     * Returns mocks that match the argument type, if not possible assigns null.
68     */
69    static class SimpleArgumentResolver implements ConstructorArgumentResolver {
70        final Set<Object> objects;
71
72        public SimpleArgumentResolver(Set<Object> objects) {
73            this.objects = objects;
74        }
75
76        public Object[] resolveTypeInstances(Class<?>... argTypes) {
77            List<Object> argumentInstances = new ArrayList<Object>(argTypes.length);
78            for (Class<?> argType : argTypes) {
79                argumentInstances.add(objectThatIsAssignableFrom(argType));
80            }
81            return argumentInstances.toArray();
82        }
83
84        private Object objectThatIsAssignableFrom(Class<?> argType) {
85            for (Object object : objects) {
86                if(argType.isAssignableFrom(object.getClass())) return object;
87            }
88            return null;
89        }
90    }
91
92}
93