/*
 * Copyright 2015 yihtserns.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.yihtserns.logback.spring.config;

import ch.qos.logback.ext.spring.ApplicationContextHolder;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

/**
 * @author yihtserns
 */
public class LogbackNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("appender", new AbstractSingleBeanDefinitionParser() {

            @Override
            protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
                registerContextHolderIfNotYet(parserContext);

                builder.setFactoryMethod("assemble");

                String name = element.getAttribute("name");
                if (StringUtils.hasText(name)) {
                    builder.addPropertyValue("name", name);
                }

                String appenderClassName = element.getAttribute("class");
                BeanDefinition appenderBd = BeanDefinitionBuilder.genericBeanDefinition(appenderClassName)
                        .setDestroyMethodName("stop")
                        .getBeanDefinition();
                builder.addConstructorArgValue(appenderBd);

                ManagedList<ManagedMap<String, Object>> property2ValueList = new ManagedList<ManagedMap<String, Object>>();
                for (Element childElement : DomUtils.getChildElements(element)) {
                    String localName = childElement.getLocalName();
                    if ("appender-ref".equals(localName)) {
                        String appenderName = childElement.getAttribute("ref");

                        ManagedMap<String, Object> property2Value = new ManagedMap<String, Object>();
                        property2Value.put("appender", new RuntimeBeanReference(appenderName));

                        property2ValueList.add(property2Value);
                    } else {
                        Object childValue;
                        if (StringUtils.hasText(childElement.getAttribute("class"))) {
                            ParserContext childParserContext = new ParserContext(
                                    parserContext.getReaderContext(),
                                    parserContext.getDelegate(),
                                    builder.getRawBeanDefinition());
                            childValue = parse(childElement, childParserContext);
                        } else {
                            childValue = childElement.getTextContent();
                        }

                        ManagedMap<String, Object> property2Value = new ManagedMap<String, Object>();
                        property2Value.put(localName, childValue);

                        property2ValueList.add(property2Value);
                    }
                }
                builder.addConstructorArgValue(property2ValueList);
            }

            private void registerContextHolderIfNotYet(ParserContext parserContext) {
                final String contextHolderBeanName = "logback.applicationContextHolder";

                if (!parserContext.getRegistry().containsBeanDefinition(contextHolderBeanName)) {
                    BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(ApplicationContextHolder.class).getBeanDefinition();
                    parserContext.registerBeanComponent(new BeanComponentDefinition(bd, contextHolderBeanName));
                }
            }

            @Override
            protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
                return element.getAttribute("name");
            }

            @Override
            protected Class<?> getBeanClass(Element element) {
                return LogbackObjectPropertiesAssembler.class;
            }
        });
    }

}

@@ -2911,24 +2911,111 @@
 ocalName();%0D
+%0A%0D%0A                    String propertyName;%0D%0A                    Object propertyValue;%0D
 %0A           
@@ -3175,84 +3175,33 @@
     
-ManagedMap%3CString, Object%3E property2Value = new ManagedMap%3CString, Object%3E()
+propertyName = %22appender%22
 ;%0D%0A 
@@ -3235,30 +3235,15 @@
 erty
-2
 Value
-.put(%22appender%22,
+ =
  new
@@ -3281,176 +3281,37 @@
 ame)
-);%0D%0A%0D%0A                        property2ValueList.add(property2Value);%0D%0A                    %7D else %7B%0D%0A                        Object childValue;%0D%0A                       
+;%0D%0A                    %7D else
  if 
@@ -3367,32 +3367,73 @@
 lass%22))) %7B%0D%0A    
+                    // Complex property%0D%0A
                 
@@ -3495,20 +3495,16 @@
 ntext(%0D%0A
-    
         
@@ -3586,36 +3586,32 @@
                 
-    
 parserContext.ge
@@ -3620,28 +3620,24 @@
 elegate(),%0D%0A
-    
             
@@ -3686,24 +3686,32 @@
 nition());%0D%0A
+%0D%0A      
             
@@ -3712,39 +3712,83 @@
             
-          child
+propertyName = localName;%0D%0A                        property
 Value = pars
@@ -3841,28 +3841,24 @@
             
-    
 %7D else %7B%0D%0A  
@@ -3877,23 +3877,117 @@
     
+      
-          child
+// Simple property%0D%0A                        propertyName = localName;%0D%0A                        property
 Valu
@@ -4038,37 +4038,29 @@
             
-    
 %7D%0D%0A%0D%0A
-    
             
@@ -4158,36 +4158,32 @@
                 
-    
 property2Value.p
@@ -4189,24 +4189,30 @@
 put(
-localName, child
+propertyName, property
 Valu
@@ -4214,28 +4214,24 @@
 yValue);%0D%0A%0D%0A
-    
             
@@ -4279,39 +4279,16 @@
 alue);%0D%0A
-                    %7D%0D%0A
         

Remove duplication.