/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/jdomobjects/XNodeRevisionDescriptor.java,v 1.4 2004/09/15 14:58:26 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2004/09/15 14:58:26 $
 *
 * ====================================================================
 *
 * 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.*;

import org.apache.slide.common.UriPath;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeProperty.NamespaceCache;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.store.tamino.common.IDescriptors;
import org.apache.slide.store.tamino.common.IDescriptorsHandler;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XException;
import org.apache.slide.util.XMLValue;
import org.apache.slide.util.XUri;
import org.apache.slide.webdav.util.BindConstants;
import org.jdom.Element;

/**
 * Serializes NodeRevisionDescriptor to and from XML.
 *
 * @author martin.wallmer@softwareag.com
 *
 * @version $Revision: 1.4 $
 */
public class XNodeRevisionDescriptor implements XJdom, BindConstants {
    public static List toSlideExtract(Element root) {
        List xml = root.getChild(E_DESCRIPTOR_LIST).getChildren();
        List slide = new ArrayList(xml.size());
        Iterator it = xml.iterator();
        while (it.hasNext()) {
            slide.add(toSlide((Element)it.next()));
        }
        return slide;
    }
    
    public static Element toXmlList (List revisionDescriptorList, String tsdLanguage) {
        Element root;
        Iterator iter;
        
        root = new Element(E_DESCRIPTOR_LIST);
        iter = revisionDescriptorList.iterator();
        while (iter.hasNext()) {
            root.addContent(toXml((NodeRevisionDescriptor) iter.next(), tsdLanguage));
        }
        return root;
    }
    
    public static void toStreamList(XmlStream xs, List revisionDescriptorList, String tsdLanguage) {
        Iterator iter;
        
        xs.addStartElement(E_DESCRIPTOR_LIST);
        xs.addEndElement();
        iter = revisionDescriptorList.iterator();
        while (iter.hasNext()) {
            toStream(xs, (NodeRevisionDescriptor) iter.next(), tsdLanguage);
        }
        xs.addCloseElement(E_DESCRIPTOR_LIST);
    }
    
    /**
     * returns the jdom aspect of this descriptor.
     *
     * @pre        revisionDescriptor != null
     * @post
     *
     * @return     Element representing this nodeRevisionDescriptor
     */
    public static Element toXml ( NodeRevisionDescriptor revisionDescriptor, String tsdLanguage ) {
        if (revisionDescriptor == null) {
            throw new XAssertionFailed();
        }
        
        Element e = new Element (E_REVISION_DESCRIPTOR);
        
        // NodeRevisionNumber
        String nodeRevsisionNumber = revisionDescriptor.getRevisionNumber().toString();
        e.setAttribute (A_REVISION_NUMBER, nodeRevsisionNumber);
        
        // branch name
        e.setAttribute (A_BRANCH_NAME, revisionDescriptor.getBranchName());
        
        Element nodePropertiesElement = new Element (E_PROPERTY_LIST);
        e.addContent (nodePropertiesElement);
        Enumeration properties = revisionDescriptor.enumerateProperties();
        
        while (properties.hasMoreElements()) {
            NodeProperty nodeProp = (NodeProperty)properties.nextElement();
            Element prop = XNodeProperty.getElement (nodeProp, tsdLanguage);
            nodePropertiesElement.addContent (prop);
        }
        
        // labels
        Element labelsElement = new Element (E_LABELS);
        e.addContent (labelsElement);
        
        Enumeration labels = revisionDescriptor.enumerateLabels();
        while (labels.hasMoreElements()) {
            String label = (String)labels.nextElement();
            Element labelElement = new Element (E_LABEL);
            labelElement.setText (label);
            labelsElement.addContent (labelElement);
        }
        
        return e;
    }
    
    public static void toStream(XmlStream xs, NodeRevisionDescriptor revisionDescriptor, String tsdLanguage)  {
        if (revisionDescriptor == null) {
            throw new XAssertionFailed();
        }
        
        xs.addStartElement(E_REVISION_DESCRIPTOR);
        
        // NodeRevisionNumber
        String nodeRevsisionNumber = revisionDescriptor.getRevisionNumber().toString();
        xs.addAttribute(A_REVISION_NUMBER, nodeRevsisionNumber);
        
        // branch name
        xs.addAttribute(A_BRANCH_NAME, revisionDescriptor.getBranchName());
        xs.addEndElement();
        
        xs.addStartElement(E_PROPERTY_LIST);
        xs.addEndElement();
        Enumeration properties = revisionDescriptor.enumerateProperties();
        
        while (properties.hasMoreElements()) {
            NodeProperty nodeProp = (NodeProperty)properties.nextElement();
            XNodeProperty.toStream (xs, nodeProp, tsdLanguage);
            
            // @@@@@@@@@@@ Hack for version-history @@@@@@@@@@@@@@@@@@@@
            // This is to make the DAV:locate-by-history report faster
            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            if (("checked-in".equals(nodeProp.getName()) || "checked-out".equals(nodeProp.getName())) &&
                "DAV:".equals(nodeProp.getNamespace())) {
                
                String v = (String)nodeProp.getValue();
                String p = v.substring(v.indexOf("/"), v.lastIndexOf('<'));
                UriPath up = new UriPath(p);
                NodeProperty np = new NodeProperty("version-history", up.parent().toString(), "DAV:");
                XNodeProperty.toStream(xs, np, tsdLanguage);
                //System.out.println("@@@ Added "+np.getNamespace()+":"+np.getName()+"="+np.getValue());
            }
        }
        xs.addCloseElement(E_PROPERTY_LIST);
        
        // labels
        xs.addStartElement(E_LABELS);
        xs.addEndElement();
        
        Enumeration labels = revisionDescriptor.enumerateLabels();
        while (labels.hasMoreElements()) {
            String label = (String)labels.nextElement();
            xs.addStartElement(E_LABEL);
            xs.addText(label);
            xs.addEndElement();
            xs.addCloseElement(E_LABEL);
        }
        xs.addCloseElement(E_LABELS);
        xs.addCloseElement(E_REVISION_DESCRIPTOR);
    }
    
    /**
     * creates the slide aspect of this NodeRevisionDescriptor
     */
    public static NodeRevisionDescriptor toSlide(Element jdomAspect) {
        if (jdomAspect == null) {
            throw new XAssertionFailed();
        }
        NodeRevisionDescriptor revisionDescriptor;
        
        // revision number
        NodeRevisionNumber revisionNumber =
            new NodeRevisionNumber (jdomAspect.getAttributeValue(A_REVISION_NUMBER));
        
        // branch name
        String branchName = jdomAspect.getAttributeValue(A_BRANCH_NAME);
        
        // labels
        List labelList = jdomAspect.getChild (E_LABELS).getChildren();
        Vector labels = new Vector (labelList.size());
        Iterator it = labelList.iterator();
        while (it.hasNext()) {
            String label = ((Element) it.next()).getText();
            labels.add (label);
        }
        
        // properties
        List propertyList = jdomAspect.getChild (E_PROPERTY_LIST).getChildren();
        
        Hashtable properties = new Hashtable (propertyList.size());
        
        // create NodeRevisionDescriptor
        revisionDescriptor = new NodeRevisionDescriptor (revisionNumber,
                                                         branchName,
                                                         labels,
                                                         properties);
        // set the properties
        it = propertyList.iterator();
        while (it.hasNext()) {
            NodeProperty nodeProp = XNodeProperty.toSlide((Element) it.next());
            nodeProp = getNamespaceMigratedProperty(nodeProp);
            revisionDescriptor.setProperty (nodeProp);
        }
        
        return revisionDescriptor;
    }
    
    /**
     * If the given NodeProperty is one of the proprietary Tamino properties
     * (e.g. <code>xdavContentId</code>) and their namespace
     * is <code>DAV:</code>, a NodeProperty with the same name and value but
     * with the {@link org.apache.slide.store.tamino.common.XGlobals#TAMINO_NAMESPACE_URI
     * Tamino namespace} is returned. Otherwise the given property
     * is returned without modification.
     *
     * @param      property  the NodeProperty to migrate if necessary.
     *
     * @return     the migrated or the given property (see description).
     */
    private static NodeProperty getNamespaceMigratedProperty(NodeProperty property) {
        
        NodeProperty migratedProperty = property;
        
        if (XGlobals.CONTENT_ID.equals(property.getName()) &&
            NamespaceCache.DEFAULT_URI.equals(property.getNamespace()) ) {
            
            migratedProperty = new NodeProperty(property.getName(),
                                                property.getValue(),
                                                XGlobals.TAMINO_NAMESPACE_URI);
            migratedProperty.setKind( NodeProperty.Kind.PROTECTED );
        }
        
        return migratedProperty;
    }
    
    
    //-- misc
    
    /**
     * locate a nodeRevisionDescriptor within descriptorListElement,
     * identified by nodeRevisionNumber.
     *
     * @pre        descriptorList != null
     * @pre        nodeRevisionNumber != null
     * @pre        nodeRevisionNumber.length gt 0
     * @post       true
     *
     * @param      descriptorList   The List of NodeRevisionDescriptor objects
     * @param      nodeRevisionNumber a String identifying the nodeRevisionNumber
     *             of the searched nodeRevisionNumber
     *
     * @return     the element specifiying the searched nodeRevisionNumberDescriptor
     *             or null if not found
     *
     */
    public static NodeRevisionDescriptor findElement(List descriptorList, NodeRevisionNumber nodeRevisionNumber) {
        NodeRevisionDescriptor current;
        Iterator it = descriptorList.iterator();
        while (it.hasNext()) {
            current = (NodeRevisionDescriptor)it.next();
            if (nodeRevisionNumber.equals(current.getRevisionNumber())) {
                return current;
            }
        }
        return null;
    }
    
    /**
     * Return true if the given node revision descriptor is a collection.
     * @param nrd node revision descriptor
     * @return true if the given node revision descriptor is a collection
     */
    public static boolean isCollection( NodeRevisionDescriptor nrd ) {
        return nrd.propertyValueContains(NodeRevisionDescriptor.RESOURCE_TYPE ,"collection");
    }
    
    /**
     * Method getPropertyValue
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     * @param    pname               a  String
     * @param    namespace           a  String
     *
     * @return   an Object
     *
     */
    private static Object getPropertyValue( NodeRevisionDescriptor nrd, String pname, String namespace) {
        Object result = null;
        if( nrd == null )
            return result;
        
        NodeProperty p = null;
        
        if (namespace == null)
            p = nrd.getProperty( pname );
        else
            p = nrd.getProperty( pname, namespace );
        
        if( p == null )
            return result;
        
        return p.getValue();
    }
    
    //-- TODO: move somewhere else?
    
    public static void addSpecialProperties(
        IDescriptorsHandler handler, ObjectNode objectNode, NodeRevisionDescriptor nrd)
        throws XException {
        nrd.setProperty(P_RESOURCE_ID, getXmlResourceId(objectNode));
        nrd.setProperty(P_PARENT_SET, getXmlParentSet(handler, objectNode));
    }
    
    public static String getXmlResourceId(ObjectNode objectNode) throws XException {
        XMLValue r = new XMLValue();
        Element hrefElm = new Element( E_HREF, NamespaceCache.DEFAULT_NAMESPACE );
        hrefElm.setText( objectNode.getUuri() );
        r.add( hrefElm );
        return r.toString();
    }
    
    public static String getXmlParentSet(IDescriptorsHandler handler, ObjectNode objectNode) throws XException {
        XMLValue result;
        Element parent;
        Element child;
        Enumeration parentBindings;
        ObjectNode.Binding binding;
        
        result = new XMLValue();
        parentBindings = objectNode.enumerateParentBindings();
        while (parentBindings.hasMoreElements()) {
            binding = (ObjectNode.Binding) parentBindings.nextElement();
            parent = new Element(E_PARENT, NamespaceCache.DEFAULT_NAMESPACE);
            child = new Element(E_HREF, NamespaceCache.DEFAULT_NAMESPACE);
            child.setText(getOneUri(handler, objectNode, binding.getUuri()));
            parent.addContent(child);
            child = new Element(E_SEGMENT, NamespaceCache.DEFAULT_NAMESPACE);
            child.setText(binding.getName());
            parent.addContent(child);
            result.add(parent);
        }
        
        return result.toString();
    }
    
    /**
     ** @param uuri to obtain uuri for
     **/
    public static String getOneUri(IDescriptorsHandler handler, ObjectNode childObjectNode, String uuri) throws XException {
        if (handler.useBinding() && !XUuri.isStoreUuri(childObjectNode.getUuri())) {
            return getOneUri(handler, uuri);
        }
        else {
            // if childObjectNode is the root of a store, uuri.equals(""),
            // thus we cannot call getOneParentUri because
            // we cannot resolve the uuri
            return new XUri(childObjectNode.getUri()).getParent().toString();
        }
    }
    
    public static String getOneUri(IDescriptorsHandler handler, String uuri) throws XException {
        IDescriptors desc;
        String result;
        ObjectNode objectNode;
        Enumeration en;
        ObjectNode.Binding binding;
        String segment;
        
        result = "";
        while (!XUuri.isStoreUuri(uuri)) {
            desc = handler.lookup(null, uuri, XTLock.NO_LOCK);
            objectNode = desc.getUuriObjectNode();
            en = objectNode.enumerateParentBindings();
            if (!en.hasMoreElements()) {
                throw new IllegalStateException();
            }
            binding = (ObjectNode.Binding) en.nextElement();
            segment = binding.getName();
            result = "/" + segment + result;
            uuri = binding.getUuri();
        }
        return XUuri.getStoreUri(uuri) + result;
    }
    
    /**
     * @post result == nrd
     *
     * @param nrd
     *
     * @return the nrd argument
     */
    public static void removeSpecialProperties(NodeRevisionDescriptor nrd) {
        nrd.removeProperty(P_RESOURCE_ID);
        nrd.removeProperty(P_PARENT_SET);
    }
}



