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.stubbing.defaultanswers;
6
7import org.mockito.MockSettings;
8import org.mockito.internal.InternalMockHandler;
9import org.mockito.internal.MockitoCore;
10import org.mockito.internal.creation.settings.CreationSettings;
11import org.mockito.internal.stubbing.InvocationContainerImpl;
12import org.mockito.internal.stubbing.StubbedInvocationMatcher;
13import org.mockito.internal.util.MockUtil;
14import org.mockito.internal.util.reflection.GenericMetadataSupport;
15import org.mockito.invocation.InvocationOnMock;
16import org.mockito.stubbing.Answer;
17
18import java.io.Serializable;
19
20import static org.mockito.Mockito.withSettings;
21
22/**
23 * Returning deep stub implementation.
24 *
25 * Will return previously created mock if the invocation matches.
26 *
27 * <p>Supports nested generic information, with this answer you can write code like this :
28 *
29 * <pre class="code"><code class="java">
30 *     interface GenericsNest&lt;K extends Comparable&lt;K&gt; & Cloneable&gt; extends Map&lt;K, Set&lt;Number&gt;&gt; {}
31 *
32 *     GenericsNest&lt;?&gt; mock = mock(GenericsNest.class, new ReturnsGenericDeepStubs());
33 *     Number number = mock.entrySet().iterator().next().getValue().iterator().next();
34 * </code></pre>
35 * </p>
36 *
37 * @see org.mockito.Mockito#RETURNS_DEEP_STUBS
38 * @see org.mockito.Answers#RETURNS_DEEP_STUBS
39 */
40public class ReturnsDeepStubs implements Answer<Object>, Serializable {
41
42    private static final long serialVersionUID = -7105341425736035847L;
43
44    private MockitoCore mockitoCore = new MockitoCore();
45    private ReturnsEmptyValues delegate = new ReturnsEmptyValues();
46
47    public Object answer(InvocationOnMock invocation) throws Throwable {
48        GenericMetadataSupport returnTypeGenericMetadata =
49                actualParameterizedType(invocation.getMock()).resolveGenericReturnType(invocation.getMethod());
50
51        Class<?> rawType = returnTypeGenericMetadata.rawType();
52        if (!mockitoCore.isTypeMockable(rawType)) {
53            return delegate.returnValueFor(rawType);
54        }
55
56        return getMock(invocation, returnTypeGenericMetadata);
57    }
58
59    private Object getMock(InvocationOnMock invocation, GenericMetadataSupport returnTypeGenericMetadata) throws Throwable {
60    	InternalMockHandler<Object> handler = new MockUtil().getMockHandler(invocation.getMock());
61    	InvocationContainerImpl container = (InvocationContainerImpl) handler.getInvocationContainer();
62
63        // matches invocation for verification
64        for (StubbedInvocationMatcher stubbedInvocationMatcher : container.getStubbedInvocations()) {
65    		if(container.getInvocationForStubbing().matches(stubbedInvocationMatcher.getInvocation())) {
66    			return stubbedInvocationMatcher.answer(invocation);
67    		}
68		}
69
70        // deep stub
71        return recordDeepStubMock(createNewDeepStubMock(returnTypeGenericMetadata), container);
72    }
73
74    /**
75     * Creates a mock using the Generics Metadata.
76     *
77     * <li>Finally as we want to mock the actual type, but we want to pass along the contextual generics meta-data
78     * that was resolved for the current return type, for this to happen we associate to the mock an new instance of
79     * {@link ReturnsDeepStubs} answer in which we will store the returned type generic metadata.
80     *
81     * @param returnTypeGenericMetadata The metadata to use to create the new mock.
82     * @return The mock
83     */
84    private Object createNewDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata) {
85        return mockitoCore.mock(
86                returnTypeGenericMetadata.rawType(),
87                withSettingsUsing(returnTypeGenericMetadata)
88        );
89    }
90
91    private MockSettings withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata) {
92        MockSettings mockSettings =
93                returnTypeGenericMetadata.rawExtraInterfaces().length > 0 ?
94                withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces())
95                : withSettings();
96
97        return mockSettings
98                .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata));
99    }
100
101    private ReturnsDeepStubs returnsDeepStubsAnswerUsing(final GenericMetadataSupport returnTypeGenericMetadata) {
102        return new ReturnsDeepStubs() {
103            @Override
104            protected GenericMetadataSupport actualParameterizedType(Object mock) {
105                return returnTypeGenericMetadata;
106            }
107        };
108    }
109
110    private Object recordDeepStubMock(final Object mock, InvocationContainerImpl container) throws Throwable {
111
112        container.addAnswer(new Answer<Object>() {
113            public Object answer(InvocationOnMock invocation) throws Throwable {
114                return mock;
115            }
116        }, false);
117
118        return mock;
119    }
120
121    protected GenericMetadataSupport actualParameterizedType(Object mock) {
122        CreationSettings mockSettings = (CreationSettings) new MockUtil().getMockHandler(mock).getMockSettings();
123        return GenericMetadataSupport.inferFrom(mockSettings.getTypeToMock());
124    }
125}
126