package no.saua.remock.internal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardMethodMetadata;

import java.util.Set;

public class RemockBeanFactory extends DefaultListableBeanFactory {

    private final static Logger log = LoggerFactory.getLogger(RemockBeanFactory.class);

    private Set<Rejecter> rejecters;

    private boolean foundEagerAnnotation = false;

    public RemockBeanFactory(Set<Rejecter> rejecters, boolean foundEagerAnnotation) {
        this.rejecters = rejecters;
        this.foundEagerAnnotation = foundEagerAnnotation;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        try {
            Class<?> beanClazz = null;
            String beanClassName = beanDefinition.getBeanClassName();

            // Fix for @Bean annotations in @Configuration will return a "null" beanClassName. This
            // finds the class anyway:
            if (beanClassName == null && beanDefinition instanceof AnnotatedBeanDefinition) {
                MethodMetadata factoryMethodMetadata =
                                ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata();
                if (factoryMethodMetadata instanceof StandardMethodMetadata) {
                    beanClazz =
                                    ((StandardMethodMetadata) factoryMethodMetadata).getIntrospectedMethod()
                                                    .getReturnType();
                }
            }


            if (beanClassName != null) {
                beanClazz = Class.forName(beanClassName);
            }
            if (isBeanRejected(beanName, beanClazz)) {
                log.info("Rejected bean [{}] with definiton [{}]", beanName, beanDefinition);
            } else {
                if (!foundEagerAnnotation) {
                    beanDefinition.setLazyInit(true);
                }
                super.registerBeanDefinition(beanName, beanDefinition);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public void registerSingleton(String beanName, Object bean) {
        if (isBeanRejected(beanName, bean.getClass())) {
            log.info("Rejected bean [{}] which is the singleton [{}]", beanName, bean);
        } else {
            super.registerSingleton(beanName, bean);
        }
    }

    public void registerMockBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        super.registerBeanDefinition(beanName, beanDefinition);
    }

    private boolean isBeanRejected(String beanName, Class<?> beanClazz) {
        for (Rejecter rejecter : rejecters) {
            if (rejecter.shouldReject(beanName, beanClazz)) {
                return true;
            }
        }
        return false;
    }
}

@@ -27,16 +27,72 @@
 ernal;%0A%0A
+import java.lang.reflect.Method;%0Aimport java.util.Set;%0A%0A
 import o
@@ -108,16 +108,16 @@
 Logger;%0A
-
 import o
@@ -473,39 +473,16 @@
 adata;%0A%0A
-import java.util.Set;%0A%0A
 public c
@@ -1624,27 +1624,191 @@
         
-beanClazz =
+StandardMethodMetadata methodMetaData = (StandardMethodMetadata) factoryMethodMetadata;%0A                    beanClazz = methodMetaData.getIntrospectedMethod().getReturnType();
 %0A       
@@ -1808,34 +1808,41 @@
                 
-  
+%7D else %7B%0A
                 
@@ -1847,78 +1847,757 @@
     
-((StandardMethodMetadata) factoryMethodMetadata).getIntrospect
+  // Try to find the method directly from the defining class/method%0A                    String declaringClassName = factoryMethodMetadata.getDeclaringClassName();%0A                    Class%3C?%3E factoryClazz = Class.forName(declaringClassName);%0A%0A                    // Note that MethodMetadata does not give us the exact method, we have to loop through to find%0A                    // one which matches the name -- this is not optimal. But it should not matter much, the only thing%0A                    // which may trip this up is having to classes with same name but different return values which is%0A                    // not something we would expect in (sane) @Configuration classes.%0A                    for (Method m: factoryClazz.getDeclar
 edMethod
 ()%0A 
@@ -2592,18 +2592,22 @@
 edMethod
+s
 ()
+) %7B
 %0A       
@@ -2615,33 +2615,97 @@
                 
- 
+if (factoryMethodMetadata.getMethodName().equals(m.getName())) %7B%0A
                 
@@ -2715,16 +2715,30 @@
         
+ beanClazz = m
 .getRetu
@@ -2739,32 +2739,80 @@
 etReturnType();%0A
+                        %7D%0A                    %7D%0A
                 
@@ -2824,25 +2824,24 @@
          %7D%0A%0A
-%0A
             
@@ -2949,32 +2949,53 @@
             if (
+beanClazz != null && 
 isBeanRejected(b
@@ -3103,32 +3103,32 @@
 eanDefinition);%0A
-
             %7D el
@@ -3124,32 +3124,210 @@
        %7D else %7B%0A
+                if (beanClazz == null) %7B%0A                    log.warn(%22Unable to find beanclass for bean %5B%7B%7D%5D with definition %5B%7B%7D%5D%22, beanName, beanDefinition);%0A                %7D%0A
                 

Fix nullpointer when finding beanClass with Spring caching (and other AOP type spring libs)