/*
 * $Id: ComponentDelegatingDefinitionParser.java 11379 2008-03-17 02:46:56Z dfeist $
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.config.spring.parsers.specific;

import org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser;
import org.mule.config.spring.parsers.MuleDefinitionParser;
import org.mule.config.spring.parsers.PreProcessor;
import org.mule.config.spring.parsers.assembly.configuration.PropertyConfiguration;
import org.mule.config.spring.parsers.delegate.AbstractParallelDelegatingDefinitionParser;
import org.mule.config.spring.util.SpringXMLUtils;
import org.mule.util.StringUtils;

import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * Allows for parsing either a shortcut component configuration by delegating to two
 * different component parses depending on the existence of the class attribute. If
 * the class attribute is used then an embedded object factory element cannot be
 * used.
 * 
 * <pre>
 * &lt;component class=&quot;&quot;&gt;
 * </pre>
 * 
 * or one with an embedded object factory element.
 * 
 * <pre>
 * &lt;component&gt;
 *     &lt;singleon-object class=&quot;..&quot;/&gt;
 * &lt;/component&gt;
 * </pre>
 */
public class ComponentDelegatingDefinitionParser extends AbstractParallelDelegatingDefinitionParser
{

    private MuleDefinitionParser normalConfig;
    private MuleDefinitionParser shortcutConfig;

    public ComponentDelegatingDefinitionParser(Class clazz)
    {
        normalConfig = new ComponentDefinitionParser(clazz);
        shortcutConfig = new ShortcutComponentDefinitionParser(clazz);
        addDelegate(normalConfig);
        addDelegate(shortcutConfig);
        registerPreProcessor(new CheckExclusiveClassAttributeObjectFactory());
    }

    protected MuleDefinitionParser getDelegate(Element element, ParserContext parserContext)
    {
        if (StringUtils.isEmpty(element.getAttribute(AbstractMuleBeanDefinitionParser.ATTRIBUTE_CLASS)))
        {
            return normalConfig;
        }
        else
        {
            return shortcutConfig;
        }
    }

    /**
     * Given that the service object-factory is extensible and new object factory
     * types can be implemented and used by substitution, the only extensible way of
     * checking for the existence of an object-factory child element is by exclusion.<br>
     * This pre-processor checks for the existence of a <i>"class"</i> attribute on
     * the service, and throws an exception if the service has any child elements
     * that are not binding's.
     */
    class CheckExclusiveClassAttributeObjectFactory implements PreProcessor
    {

        private static final String BINDING_CHILD_ELEMENT = "binding";
        private static final String POOLING_PROFILE_CHILD_ELEMENT = "pooling-profile";
        private static final String LIFECYCLE_ADAPTER_FACTORT_CHILD_ELEMENT = "lifecycle-adapter-factory";
        private static final String ENTRY_POINT_RESOLVER_CHILD_ELEMENT = "entry-point-resolver";
        private static final String ENTRY_POINT_RESOLVER_SET_CHILD_ELEMENT = "entry-point-resolver-set";

        public void preProcess(PropertyConfiguration config, Element element)
        {
            NamedNodeMap attributes = element.getAttributes();
            for (int i = 0; i < attributes.getLength(); i++)
            {
                String alias = SpringXMLUtils.attributeName((Attr) attributes.item(i));
                if (alias.equals(AbstractMuleBeanDefinitionParser.ATTRIBUTE_CLASS))
                {
                    for (int j = 0; j < element.getChildNodes().getLength(); j++)
                    {
                        Node child = element.getChildNodes().item(j);
                        if (child instanceof Element
                            && !(child.getLocalName().equals(BINDING_CHILD_ELEMENT)
                                 || child.getLocalName().equals(POOLING_PROFILE_CHILD_ELEMENT)
                                 || child.getLocalName().equals(ENTRY_POINT_RESOLVER_CHILD_ELEMENT)
                                 || child.getLocalName().equals(ENTRY_POINT_RESOLVER_SET_CHILD_ELEMENT) || child.getLocalName()
                                .endsWith(LIFECYCLE_ADAPTER_FACTORT_CHILD_ELEMENT)))
                        {
                            StringBuffer message = new StringBuffer("The child element '");
                            message.append(child.getLocalName());
                            message.append("' cannot appear with the 'class' attribute");
                            message.append(" in element ");
                            message.append(SpringXMLUtils.elementToString(element));
                            message.append(".");
                            throw new CheckExclusiveClassAttributeObjectFactoryException(message.toString());
                        }
                    }

                }
            }
        }
    }

    class CheckExclusiveClassAttributeObjectFactoryException extends IllegalStateException
    {
        private static final long serialVersionUID = 4625276914151932111L;

        private CheckExclusiveClassAttributeObjectFactoryException(String message)
        {
            super(message);
        }
    }

}
