package it.nch.skill.xml.soap;

import java.io.Writer;
import java.io.IOException;
import java.util.Enumeration;
import javax.swing.tree.DefaultMutableTreeNode;
// xml
import org.w3c.dom.Element;
import org.w3c.dom.Node;
// soap
import org.apache.soap.Constants;
import org.apache.soap.util.Bean;
import org.apache.soap.util.StringUtils;
import org.apache.soap.rpc.SOAPContext;
import org.apache.soap.util.xml.QName;
import org.apache.soap.util.xml.NSStack;
import org.apache.soap.util.xml.DOMUtils;
import org.apache.soap.util.xml.Serializer;
import org.apache.soap.util.xml.Deserializer;
import org.apache.soap.util.xml.XMLJavaMappingRegistry;
import org.apache.soap.encoding.soapenc.SoapEncUtils;


/**
 * XML SOAP serializer/deserializer for javax.swing.tree.DefaultMutableTreeNode.
 *
 * @version 1.0
 * @author  Gianpiero Caretti (gpcaretti@rumors.it)
 */
public class DefaultMutableTreeNodeSerializer implements Serializer, Deserializer {

    private static final String NODE_TAG = "node";
    private static final String ITEM_TAG = "item";
    private static final String ALLOW_CHILDREN_TAG = "allowchildren";

    /**
     * marshall
     */
    public void marshall(String inScopeEncStyle, Class javaType, Object src,
            Object context, Writer sink, NSStack nsStack,
            XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                    throws IllegalArgumentException, IOException {

        // 1. If it's not a DefaultMutableTreeNode throws an exception
        if ((src != null) && !(src instanceof  DefaultMutableTreeNode))
            throw new IllegalArgumentException("Tried to pass a '" +
                src.getClass().toString() + "' to DefaultMutableTreeNodeSerializer");

        // 2. if it's null generete a null structure and return
        if (src == null) {
            nsStack.pushScope();
            SoapEncUtils.generateNullStructure(
                    inScopeEncStyle, javaType, context, sink, nsStack, xjmr);
            nsStack.popScope();
            return;
        }

        // 3. OK. If you're here, read and XMLize it
        DefaultMutableTreeNode tree  = (DefaultMutableTreeNode)src;
        String strAllowsChildren = String.valueOf(tree.getAllowsChildren());

        nsStack.pushScope();
        SoapEncUtils.generateStructureHeader(
                inScopeEncStyle, javaType, context, sink, nsStack, xjmr);
        sink.write(StringUtils.lineSeparator);

        nsStack.pushScope();

        // 3.1 marshall the current root user object
        Object value = tree.getUserObject();
        if (value == null) {
            SoapEncUtils.generateNullStructure(
                    inScopeEncStyle, Object.class, ITEM_TAG, sink, nsStack, xjmr);
        }
        else {
            xjmr.marshall(
                    inScopeEncStyle, value.getClass(), value,
                    ITEM_TAG, sink, nsStack, ctx);
        }
        sink.write(StringUtils.lineSeparator);

        // 3.2 marshall the allowsChildren attribute
        boolean allowsChildren = tree.getAllowsChildren();
        xjmr.marshall(
                inScopeEncStyle, boolean.class, new Boolean(allowsChildren),
                ALLOW_CHILDREN_TAG, sink, nsStack, ctx);

        sink.write(StringUtils.lineSeparator);

        // 3.3 marshall the children recursively
        for (Enumeration enum = tree.children(); enum.hasMoreElements(); ) {
            xjmr.marshall(
                    inScopeEncStyle, javaType,
                    (DefaultMutableTreeNode)enum.nextElement(),
                    NODE_TAG, sink, nsStack, ctx);
        }

        nsStack.popScope();

        // 4. close tags and exit
        sink.write("</" + context + '>');
        nsStack.popScope();
    }


    /**
     * unmarshall
     */
    public Bean unmarshall(
            String inScopeEncStyle, QName elementType,
            Node src, XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                    throws IllegalArgumentException {

        Element root = (Element)src;

        if (SoapEncUtils.isNull(root)) {
            return new Bean(DefaultMutableTreeNode.class, null);
        }
        
        DefaultMutableTreeNode tree = new DefaultMutableTreeNode();

        Element tempEl = DOMUtils.getFirstChildElement(root);
        while (tempEl != null) {

            String declEncStyle = DOMUtils.getAttributeNS(
                tempEl, Constants.NS_URI_SOAP_ENV, Constants.ATTR_ENCODING_STYLE);
            String actualEncStyle =
                (declEncStyle != null) ? declEncStyle : inScopeEncStyle;
            QName declItemType = SoapEncUtils.getTypeQName(tempEl);
            QName actualItemType = declItemType;

            Bean itemBean = xjmr.unmarshall(
                    actualEncStyle, actualItemType, tempEl, ctx);

            // if it's the user object, set it
            if ( ITEM_TAG.equals(tempEl.getTagName()) ) {
                tree.setUserObject(itemBean.value);
            }
            // otherwise if it's the "allow children" attribute, set it
            else if ( ALLOW_CHILDREN_TAG.equals(tempEl.getTagName())  &&
                    itemBean.type.equals(boolean.class) ) {
                tree.setAllowsChildren(((Boolean)itemBean.value).booleanValue());
            }
            // otherwise it'd be another node
            else if ( NODE_TAG.equals(tempEl.getTagName()) &&
                    itemBean.type.equals(DefaultMutableTreeNode.class) ) {
                tree.add((DefaultMutableTreeNode)itemBean.value);
            }
            else {
                throw new IllegalArgumentException(
                    "Tried to pass a '" + tempEl.getTagName() +
                    "' tag containing a '" + itemBean.type + "' to the tree. " +
                    "Probably the source XML is malformed.");
            }

            tempEl = DOMUtils.getNextSiblingElement(tempEl);

        }
        
        return new Bean(DefaultMutableTreeNode.class, tree);
    }

}
