/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/jdomobjects/XObjectNode.java,v 1.3 2004/07/30 06:52:00 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:00 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * 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 org.apache.slide.store.tamino.jdomobjects;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
import org.apache.slide.store.tamino.common.XUriCache;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.JDom;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XUri;
import org.jdom.Attribute;
import org.jdom.Element;

/**
 * Serialize ObjectNode objects to and from XML.
 *
 * @author martin.wallmer@softwareag.com
 *
 * @version $Revision: 1.3 $
 */
public class XObjectNode implements XJdom {
    /**
     ** Serialize to Xml. Creates a jdom element from objectNode
     **/
    public static Element toXml (ObjectNode objectNode) {
        if (objectNode == null) {
            throw new XAssertionFailed();
        }
        
        // check which kind
        String kind;
        if (objectNode instanceof SubjectNode)
            kind = A_SUBJECT;
            
        else if (objectNode instanceof ActionNode)
            kind = A_ACTION;
            
        else if (objectNode instanceof LinkNode)
            kind = A_LINK;
            
        else
            throw new IllegalStateException();
        
        // create objectNode element
        Element e = new Element (E_OBJECT_NODE);
        e.setAttribute(A_KIND, kind);
        
        Element parents = new Element (E_PARENTS);
        setBindings(parents, E_PARENT_NODE, objectNode.enumerateParentBindings());
        
        // create children
        Element children = new Element (E_CHILDREN);
        setBindings(children, E_CHILD_NODE, objectNode.enumerateBindings());
        
        // create links
        Element links = new Element (E_LINKS);
        setUriRefs(links, E_CHILD_NODE, objectNode.enumerateLinks());
        
        e.addContent(parents);
        e.addContent(children);
        e.addContent(links);
        
        return e;
    }

    public static void toStream (XmlStream xs, ObjectNode objectNode) {
        if (objectNode == null) {
            throw new XAssertionFailed();
        }
        // check which kind
        String kind;
        if (objectNode instanceof SubjectNode)
            kind = A_SUBJECT;
            
        else if (objectNode instanceof ActionNode)
            kind = A_ACTION;
            
        else if (objectNode instanceof LinkNode)
            kind = A_LINK;
            
        else
            throw new IllegalStateException();
        
        // create objectNode element
        xs.addStartElement(E_OBJECT_NODE);
        xs.addAttribute(A_KIND, kind);
        xs.addEndElement();
        
        xs.addStartElement(E_PARENTS);
        xs.addEndElement();
        toStreamSetBindings(xs, E_PARENT_NODE, objectNode.enumerateParentBindings());
        xs.addCloseElement(E_PARENTS);
        
        // create children
        xs.addStartElement(E_CHILDREN);
        xs.addEndElement();
        toStreamSetBindings(xs, E_CHILD_NODE, objectNode.enumerateBindings());
        xs.addCloseElement(E_CHILDREN);
        
        // create links
        xs.addStartElement(E_LINKS);
        xs.addEndElement();
        toStreamSetUriRefs(xs, E_CHILD_NODE, objectNode.enumerateLinks());
        xs.addCloseElement(E_LINKS);
        
        xs.addCloseElement(E_OBJECT_NODE);
    }
    
    /**
     ** De-Serialize from xml
     **/
    public static ObjectNode toSlideExtract (Element root, String uuri) {
        Element jdomAspect;
        
        jdomAspect = root.getChild(E_OBJECT_NODE);
        if (jdomAspect == null) {
            return null;
        } else {
            return XObjectNode.toSlide(jdomAspect, uuri);
        }
    }
    
    public static ObjectNode toSlideExtractFrom_0_x (boolean useBinding, Element root, String uri, String uuri, XUriCache uriCache) {
        Element jdomAspect;
        
        jdomAspect = root.getChild(E_OBJECT_NODE);
        if (jdomAspect == null) {
            return null;
        } else {
            return XObjectNode.toSlideFrom_0_x(useBinding, jdomAspect, uri, uuri, uriCache);
        }
    }

    /**
     * Method toSlide
     *
     * @param    jdomAspect          an Element
     * @param    uuri                a  String
     *
     * @return   an ObjectNode
     *
     */
    public static ObjectNode toSlide (Element jdomAspect, String uuri) {
        if (jdomAspect == null) {
            throw new XAssertionFailed();
        }
        
        ObjectNode objectNode;
        // TODO: remove A_KIND attribute
        String kind = jdomAspect.getAttributeValue (A_KIND);
        Vector parents = getBindings(JDom.getXElement(jdomAspect, E_PARENTS), true);
        Vector children = getBindings(JDom.getXElement(jdomAspect, E_CHILDREN), false);
        Vector links = getLinks(jdomAspect.getChild(E_LINKS));
        if (kind.equals (A_SUBJECT))
            objectNode = new SubjectNode (uuri, children, parents, links);
        else
            throw new XAssertionFailed(kind);
        
        return objectNode;
    }
    
    private static Vector getLinks(Element listElement) {
        Iterator it;
        Vector links;
        
        links = new Vector();
        it = listElement.getChildren().iterator();
        while (it.hasNext()) {
            links.add (((Element)it.next()).getAttributeValue (A_URIREF));
        }
        return links;
    }
    
    private static Vector getBindings(Element listElement, boolean parentBindings) {
        Vector bindings;
        Iterator it;
        Element childNode;
        String name;
        String uuri;
        
        bindings = new Vector();
        it = listElement.getChildren().iterator();
        while (it.hasNext()) {
            childNode = (Element)it.next();
            name = childNode.getAttributeValue(A_NAME);
            uuri = childNode.getAttributeValue (A_URIREF);
            if (parentBindings) {
                bindings.add(new ObjectNode.ParentBinding(name, uuri));
            }
            else {
                bindings.add(new ObjectNode.Binding(name, uuri));
            }
        }
        return bindings;
    }
    
    /**
     * sets the descriptorsId.
     *
     * @param     the current state of this Descriptors object
     */
    private static void setUriRefs (Element father, String nodeName, Enumeration en) {
        while (en.hasMoreElements()) {
            Attribute uriRef = new Attribute (A_URIREF, (String)en.nextElement());
            Element childNode = new Element (nodeName);
            childNode.setAttribute(uriRef);
            father.addContent(childNode);
        }
    }

    private static void toStreamSetUriRefs (XmlStream xs, String nodeName, Enumeration en) {
        while (en.hasMoreElements()) {
            xs.addStartElement(nodeName);
            xs.addAttributeUnescaped(A_URIREF, (String)en.nextElement());
            xs.addEndElement();
            xs.addCloseElement(nodeName);
        }
    }
    
    private static void setBindings(Element father, String name, Enumeration en) {
        Element childNode;
        ObjectNode.Binding binding;
        
        while (en.hasMoreElements()) {
            binding = (ObjectNode.Binding) en.nextElement();
            childNode = new Element(name);
            childNode.setAttribute(new Attribute(A_NAME, binding.getName()));
            childNode.setAttribute(new Attribute(A_URIREF, binding.getUuri()));
            father.addContent(childNode);
        }
    }

    private static void toStreamSetBindings(XmlStream xs, String name, Enumeration en) {
        ObjectNode.Binding binding;
        
        while (en.hasMoreElements()) {
            binding = (ObjectNode.Binding) en.nextElement();
            xs.addStartElement(name);
            xs.addAttribute(A_NAME,binding.getName());
            xs.addAttributeUnescaped(A_URIREF,binding.getUuri());
            xs.addEndElement();
            xs.addCloseElement(name);
        }
    }
    
    //-- convertion
    
    public static ObjectNode toSlideFrom_0_x(boolean useBinding, Element jdomAspect, String uri, String uuri, XUriCache uriCache) {
        if (jdomAspect == null) {
            throw new XAssertionFailed();
        }
        
        ObjectNode objectNode;
        // TODO: remove A_KIND attribute
        String kind = jdomAspect.getAttributeValue (A_KIND);
        Vector parents = createParentBindings(useBinding, uri, uriCache);
        Vector children = convertChildren(useBinding, jdomAspect.getChild(E_CHILDREN), uriCache);
        Vector links = getLinks(jdomAspect.getChild(E_LINKS));
        if (kind.equals (A_SUBJECT))
            objectNode = new SubjectNode (uuri, children, parents, links);
        else if (kind.equals (A_LINK))
            throw new XAssertionFailed(); // TODO: remove this case completely
        else if (kind.equals (A_ACTION))
            throw new XAssertionFailed(); // TODO: remove this case completely
        else
            throw new IllegalStateException();
        
        return objectNode;
    }

    private static Vector convertChildren(boolean useBinding, Element listElement, XUriCache uriCache) {
        Vector bindings;
        Iterator it;
        Element childNode;
        String name;
        String uriStr;
        XUri uri;
        
        bindings = new Vector();
        it = listElement.getChildren().iterator();
        while (it.hasNext()) {
            childNode = (Element)it.next();
            uriStr = childNode.getAttributeValue (A_URIREF);
            uri = new XUri(uriStr);
            if (uri.size() == 0) {
                throw new XAssertionFailed(); // TODO: checked exception?
            }
            name = uri.segment(uri.size() - 1);
            bindings.add(new ObjectNode.Binding(name, uriCache.getOrCreate(useBinding, uriStr)));
        }
        return bindings;
    }

    private static Vector createParentBindings(boolean useBinding, String uriStr, XUriCache uriCache) {
        Vector bindings;
        String name;
        XUri uri;
        String uuri;
        
        uri = new XUri(uriStr);
        if (uri.size() == 0) {
            throw new XAssertionFailed(); // TODO: checked exception?
        }
        name = uri.segment(uri.size() - 1);
        if (XUuri.isStoreUri(uriStr)) {
            uuri = "";
        } else {
            uuri = uriCache.getOrCreate(useBinding, uri.suburi(0, uri.size() - 1).toString());
        }
        bindings = new Vector();
        bindings.add(new ObjectNode.ParentBinding(name, uuri));
        return bindings;
    }
}

