package org.springframework.security.config;

import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.util.StringUtils;

import org.w3c.dom.Element;

/**
 * @author Luke Taylor
 * @version $Id$
 */
public abstract class AbstractUserDetailsServiceBeanDefinitionParser implements BeanDefinitionParser {
    private static final String CACHE_REF = "cache-ref";
    public static final String CACHING_SUFFIX = ".caching";
    
    /**  UserDetailsService bean Id. For use in a stateful context (i.e. in AuthenticationProviderBDP) */
    private String id;
    
    protected abstract String getBeanClassName(Element element);
    
    protected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);    
    
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClassName(element)); 
        
        doParse(element, parserContext, builder);
        
        RootBeanDefinition userService = (RootBeanDefinition) builder.getBeanDefinition();
        String beanId = resolveId(element, userService, parserContext);
        
        parserContext.getRegistry().registerBeanDefinition(beanId, userService);
        
        String cacheRef = element.getAttribute(CACHE_REF);
        
        // Register a caching version of the user service if there's a cache-ref
        if (StringUtils.hasText(cacheRef)) {
            BeanDefinitionBuilder cachingUSBuilder = BeanDefinitionBuilder.rootBeanDefinition(CachingUserDetailsService.class);
            cachingUSBuilder.addConstructorArgReference(beanId);
            
            cachingUSBuilder.addPropertyValue("userCache", new RuntimeBeanReference(cacheRef));
            BeanDefinition cachingUserService = cachingUSBuilder.getBeanDefinition();
            parserContext.getRegistry().registerBeanDefinition(beanId + CACHING_SUFFIX, cachingUserService);            
        }

        id = beanId;
        
        return null;
    }

    private String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) 
            throws BeanDefinitionStoreException {

        String id = element.getAttribute("id");

        if (StringUtils.hasText(id)) {
            return id;
        }

        if(Elements.AUTHENTICATION_PROVIDER.equals(element.getParentNode().getNodeName())) {
            return parserContext.getReaderContext().generateBeanName(definition);
        }

        // If top level, use the default name or throw an exception if already used
        if (parserContext.getRegistry().containsBeanDefinition(BeanIds.USER_DETAILS_SERVICE)) {
            throw new BeanDefinitionStoreException("No id supplied and another " +
                    "bean is already registered as " + BeanIds.USER_DETAILS_SERVICE);
        }

        return BeanIds.USER_DETAILS_SERVICE;
    }

    String getId() {
        return id;
    }
}

@@ -300,16 +300,90 @@
 erence;%0A
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;%0A
 import o
@@ -1002,20 +1002,16 @@
 ching%22;%0A
-    
 %0A    /**
@@ -1132,20 +1132,16 @@
 ing id;%0A
-    
 %0A    pro
@@ -1198,20 +1198,16 @@
 ement);%0A
-    
 %0A    pro
@@ -1312,25 +1312,17 @@
 uilder);
-    %0A    
+%0A
 %0A    pub
@@ -1502,26 +1502,17 @@
 ement));
- %0A        
+%0A
 %0A       
@@ -1554,24 +1554,16 @@
 ilder);%0A
-        
 %0A       
@@ -1718,24 +1718,16 @@
 ntext);%0A
-        
 %0A       
@@ -1800,32 +1800,119 @@
 rvice);%0A        
+parserContext.registerBeanComponent(new BeanComponentDefinition(userService, beanId));%0A
 %0A        String 
@@ -1955,24 +1955,16 @@
 E_REF);%0A
-        
 %0A       
@@ -2275,28 +2275,16 @@
 eanId);%0A
-            
 %0A       
@@ -2566,16 +2566,17 @@
 ervice);
+%0A
         
@@ -2571,32 +2571,142 @@
 e);%0A            
+parserContext.registerBeanComponent(new BeanComponentDefinition(cachingUserService, beanId + CACHING_SUFFIX));
 %0A        %7D%0A%0A    
@@ -2722,24 +2722,16 @@
 beanId;%0A
-        
 %0A       
@@ -2860,17 +2860,16 @@
 Context)
- 
 %0A       

Add component definition registration for tooling.