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.Mockito;
9import org.mockito.internal.InternalMockHandler;
10import org.mockito.internal.MockitoCore;
11import org.mockito.internal.creation.settings.CreationSettings;
12import org.mockito.internal.stubbing.InvocationContainerImpl;
13import org.mockito.internal.stubbing.StubbedInvocationMatcher;
14import org.mockito.internal.util.MockUtil;
15import org.mockito.internal.util.reflection.GenericMetadataSupport;
16import org.mockito.invocation.InvocationOnMock;
17import org.mockito.mock.MockCreationSettings;
18import org.mockito.stubbing.Answer;
19
20import java.io.IOException;
21import java.io.Serializable;
22
23import static org.mockito.Mockito.withSettings;
24
25/**
26 * Returning deep stub implementation.
27 *
28 * Will return previously created mock if the invocation matches.
29 *
30 * <p>Supports nested generic information, with this answer you can write code like this :
31 *
32 * <pre class="code"><code class="java">
33 *     interface GenericsNest&lt;K extends Comparable&lt;K&gt; & Cloneable&gt; extends Map&lt;K, Set&lt;Number&gt;&gt; {}
34 *
35 *     GenericsNest&lt;?&gt; mock = mock(GenericsNest.class, new ReturnsGenericDeepStubs());
36 *     Number number = mock.entrySet().iterator().next().getValue().iterator().next();
37 * </code></pre>
38 * </p>
39 *
40 * @see org.mockito.Mockito#RETURNS_DEEP_STUBS
41 * @see org.mockito.Answers#RETURNS_DEEP_STUBS
42 */
43public class ReturnsDeepStubs implements Answer<Object>, Serializable {
44
45    private static final long serialVersionUID = -7105341425736035847L;
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 deepStub(invocation, returnTypeGenericMetadata);
57    }
58
59    private Object deepStub(InvocationOnMock invocation, GenericMetadataSupport returnTypeGenericMetadata) throws Throwable {
60        InternalMockHandler<Object> handler = 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        // record deep stub answer
71        StubbedInvocationMatcher stubbing = recordDeepStubAnswer(
72                newDeepStubMock(returnTypeGenericMetadata, invocation.getMock()),
73                container
74        );
75
76        // deep stubbing creates a stubbing and immediately uses it
77        // so the stubbing is actually used by the same invocation
78        stubbing.markStubUsed(stubbing.getInvocation());
79
80        return stubbing.answer(invocation);
81    }
82
83    /**
84     * Creates a mock using the Generics Metadata.
85     *
86     * <li>Finally as we want to mock the actual type, but we want to pass along the contextual generics meta-data
87     * that was resolved for the current return type, for this to happen we associate to the mock an new instance of
88     * {@link ReturnsDeepStubs} answer in which we will store the returned type generic metadata.
89     *
90     * @param returnTypeGenericMetadata The metadata to use to create the new mock.
91     * @param parentMock The parent of the current deep stub mock.
92     * @return The mock
93     */
94    private Object newDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata, Object parentMock) {
95        MockCreationSettings parentMockSettings = MockUtil.getMockSettings(parentMock);
96        return mockitoCore().mock(
97                returnTypeGenericMetadata.rawType(),
98                withSettingsUsing(returnTypeGenericMetadata, parentMockSettings)
99        );
100    }
101
102    private MockSettings withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata, MockCreationSettings parentMockSettings) {
103        MockSettings mockSettings = returnTypeGenericMetadata.hasRawExtraInterfaces() ?
104                withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces())
105                : withSettings();
106
107        return propagateSerializationSettings(mockSettings, parentMockSettings)
108                .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata));
109    }
110
111    private MockSettings propagateSerializationSettings(MockSettings mockSettings, MockCreationSettings parentMockSettings) {
112        return mockSettings.serializable(parentMockSettings.getSerializableMode());
113    }
114
115    private ReturnsDeepStubs returnsDeepStubsAnswerUsing(final GenericMetadataSupport returnTypeGenericMetadata) {
116        return new ReturnsDeepStubsSerializationFallback(returnTypeGenericMetadata);
117    }
118
119    private StubbedInvocationMatcher recordDeepStubAnswer(final Object mock, InvocationContainerImpl container) {
120        DeeplyStubbedAnswer answer = new DeeplyStubbedAnswer(mock);
121        return container.addAnswer(answer, false);
122    }
123
124    protected GenericMetadataSupport actualParameterizedType(Object mock) {
125        CreationSettings mockSettings = (CreationSettings) MockUtil.getMockHandler(mock).getMockSettings();
126        return GenericMetadataSupport.inferFrom(mockSettings.getTypeToMock());
127    }
128
129
130    private static class ReturnsDeepStubsSerializationFallback extends ReturnsDeepStubs implements Serializable {
131        @SuppressWarnings("serial") // not gonna be serialized
132        private final GenericMetadataSupport returnTypeGenericMetadata;
133
134        public ReturnsDeepStubsSerializationFallback(GenericMetadataSupport returnTypeGenericMetadata) {
135            this.returnTypeGenericMetadata = returnTypeGenericMetadata;
136        }
137
138        @Override
139        protected GenericMetadataSupport actualParameterizedType(Object mock) {
140            return returnTypeGenericMetadata;
141        }
142        private Object writeReplace() throws IOException {
143            return Mockito.RETURNS_DEEP_STUBS;
144        }
145    }
146
147
148    private static class DeeplyStubbedAnswer implements Answer<Object>, Serializable {
149        @SuppressWarnings("serial") // serialization will fail with a nice message if mock not serializable
150        private final Object mock;
151
152        DeeplyStubbedAnswer(Object mock) {
153            this.mock = mock;
154        }
155        public Object answer(InvocationOnMock invocation) throws Throwable {
156            return mock;
157        }
158    }
159
160
161    private static MockitoCore mockitoCore() {
162        return LazyHolder.MOCKITO_CORE;
163    }
164
165    private static ReturnsEmptyValues delegate() {
166        return LazyHolder.DELEGATE;
167    }
168
169    private static class LazyHolder {
170        private static final MockitoCore MOCKITO_CORE = new MockitoCore();
171        private static final ReturnsEmptyValues DELEGATE = new ReturnsEmptyValues();
172    }
173}
174