/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockito.internal.stubbing.defaultanswers;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.mockito.Mockito;
import org.mockito.cglib.proxy.MethodInterceptor;
import org.mockito.cglib.proxy.MethodProxy;
import org.mockito.exceptions.Reporter;
import org.mockito.internal.creation.jmock.ClassImposterizer;
import org.mockito.internal.debugging.Location;
import org.mockito.internal.util.ObjectMethodsGuru;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

/**
 * Optional Answer that can be used with
 * {@link Mockito#mock(Class, Answer)}
 * <p>
 * This implementation can be helpful when working with legacy code. Unstubbed
 * methods often return null. If your code uses the object returned by an
 * unstubbed call you get a NullPointerException. This implementation of
 * Answer returns SmartNulls instead of nulls.
 * SmartNull gives nicer exception message than NPE because it points out the
 * line where unstubbed method was called. You just click on the stack trace.
 * <p>
 * ReturnsSmartNulls first tries to return ordinary return values (see
 * {@link ReturnsMoreEmptyValues}) then it tries to return SmartNull. If the
 * return type is not mockable (e.g. final) then ordinary null is returned.
 * <p>
 * ReturnsSmartNulls will be probably the default return values strategy in
 * Mockito 2.0
 */
public class ReturnsSmartNulls implements Answer<Object>, Serializable {

    private static final long serialVersionUID = 7618312406617949441L;

    private final class ThrowingInterceptor implements MethodInterceptor {
        private final InvocationOnMock invocation;
        private final Location location = new Location();

        private ThrowingInterceptor(InvocationOnMock invocation) {
            this.invocation = invocation;
        }

        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (new ObjectMethodsGuru().isToString(method)) {
                return "SmartNull returned by this unstubbed method call on a mock:\n" +
                        invocation.toString();
            }

            new Reporter().smartNullPointerException(invocation.toString(), location);
            return null;
        }
    }

    private final Answer<Object> delegate = new ReturnsMoreEmptyValues();

    public Object answer(final InvocationOnMock invocation) throws Throwable {
        Object defaultReturnValue = delegate.answer(invocation);
        if (defaultReturnValue != null) {
            return defaultReturnValue;
        }
        Class<?> type = invocation.getMethod().getReturnType();
        if (ClassImposterizer.INSTANCE.canImposterise(type)) {
            return ClassImposterizer.INSTANCE.imposterise(new ThrowingInterceptor(invocation), type);
        }
        return null;
    }
}

@@ -1,10 +1,11 @@
 /*
+%0D
 %0A * Copy
@@ -39,16 +39,17 @@
 ributors
+%0D
 %0A * This
@@ -110,20 +110,22 @@
 License.
+%0D
 %0A */
+%0D
 %0Apackage
@@ -238,16 +238,52 @@
 ethod;%0D%0A
+import java.lang.reflect.Modifier;%0D%0A
 import j
@@ -464,24 +464,223 @@
 .Reporter;%0D%0A
+import org.mockito.internal.IMockMaker;%0D%0Aimport org.mockito.internal.MockHandler;%0D%0Aimport org.mockito.internal.configuration.ClassPathLoader;%0D%0Aimport org.mockito.internal.creation.MockSettingsImpl;%0D%0A
 import org.m
@@ -779,16 +779,68 @@
 ation;%0D%0A
+import org.mockito.internal.invocation.Invocation;%0D%0A
 import o
@@ -1990,16 +1990,91 @@
 49441L;%0D
+%0A    private static IMockMaker mockMaker = ClassPathLoader.getMockMaker();%0D
 %0A%0D%0A    p
@@ -2116,36 +2116,35 @@
 tor 
-implements MethodInterceptor
+extends MockHandler%3CObject%3E
  %7B%0D%0A
@@ -2320,24 +2320,68 @@
 ocation) %7B%0D%0A
+            super(new MockSettingsImpl());%0D%0A
             
@@ -2450,77 +2450,41 @@
 ect 
-intercept(Object obj, Method method, Object%5B%5D args, MethodProxy proxy
+handle(Invocation nullDereference
 ) th
@@ -2552,22 +2552,43 @@
 oString(
-m
+nullDereference.getM
 ethod
+()
 )) %7B%0D%0A  
@@ -3272,159 +3272,248 @@
 if (
-ClassImposterizer.INSTANCE.canImpost
+!type.isPrimitive() && !Modifi
 er
+.
 is
-e(type)) %7B%0D%0A            return ClassImposterizer.INSTANCE.imposterise(new ThrowingInterceptor(invocation), type
+Final(type.getModifiers())) %7B%0D%0A            ThrowingInterceptor handler = new ThrowingInterceptor(invocation);%0D%0A            return mockMaker.createMock(type, new Class%5B0%5D, handler, handler.getMockSettings()
 );%0D%0A

Break ReturnsSmartNulls' dependency on cglib.