/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/wvcm/src/org/apache/wvcm/store/webdav/request/AbstractRequest.java,v 1.4 2004/12/03 14:27:39 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2004/12/03 14:27:39 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Slide", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */

package org.apache.wvcm.store.webdav.request;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.wvcm.AccessControlElement.Privilege;
import javax.wvcm.PropertyNameList;
import javax.wvcm.PropertyNameList.AttributeName;
import javax.wvcm.PropertyNameList.NestedPropertyName;
import javax.wvcm.PropertyNameList.PropertyName;
import javax.wvcm.Resource;
import javax.wvcm.SearchToken.XPathAttributeName;
import org.apache.wvcm.store.webdav.WebdavProperties;
import org.apache.wvcm.util.PropertyNameLists;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Text;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

/**
 * Base class for all handlers for request bodies.
 *
 * @author <a href="mailto:peter.nevermann@softwareag.com">Peter Nevermann</a>
 * @version $Revision: 1.4 $
 */
public abstract class AbstractRequest {
    
    protected static Namespace dnsp = Namespace.getNamespace("d", "DAV:");
    protected static String[] defaultProperties = new String[]{"resourcetype",
            "version-controlled-binding-set",
            "baseline-collection",
            "version-name",
            "workspace-checkout-set",
            "baseline-controlled-collection",
            "group-member-set",
            "displayname",
            "lockdiscovery"
    };
    
    private Document reqBodyDoc;
    private String reqBodyAsString;
    
    /**
     * Constructor
     */
    public AbstractRequest() {
    }
    
    /**
     * Set the request body document
     *
     * @param    reqBodyDoc the document
     */
    public void setReqBodyDoc(Document reqBodyDoc) {
        this.reqBodyDoc = reqBodyDoc;
//        this.reqBodyAsString = new XMLOutputter("  ", true).outputString( reqBodyDoc );
//        this.reqBodyAsString = new XMLOutputter("  ", true, "utf-8").outputString( reqBodyDoc );
        this.reqBodyAsString = null;
        try {
            StringWriter out = new StringWriter();
//JDOMbeta            new XMLOutputter("  ", true, "UTF-8").output(reqBodyDoc, out);
            Format f = Format.getPrettyFormat();
            f.setEncoding("UTF-8");
            new XMLOutputter(f).output(reqBodyDoc, out);
            this.reqBodyAsString = out.toString();
        }
        catch (IOException e) {
            throw new IllegalStateException("Could not create request body");
        }
    }
    
    /**
     * Get the request body as string
     *
     * @return   the request body
     */
    public String reqBodyAsString() {
        return reqBodyAsString;
    }
    
    /**
     * Add a DAV:prop element containing the specified properties to the
     * specified context element. If the specified property name list consists
     * only of ALL_ATTRIBUTES, a DAV:allprop element is added instead.
     *
     * @param    contextElm          an Element
     * @param    pnameList           a  PropertyNameList
     *
     */
    protected void createPropElement( Element contextElm, PropertyNameList pnameList ) {
        createPropElement(contextElm, pnameList, true);
    }
    
    /**
     * Add a DAV:prop element containing the specified properties to the
     * specified context element. If the specified property name list consists
     * only of ALL_ATTRIBUTES, a DAV:allprop element is added instead.
     *
     * @param    contextElm          an Element
     * @param    pnameList           a  PropertyNameList
     * @param    addDefaultProps     a  boolean
     *
     */
    protected void createPropElement( Element contextElm, PropertyNameList pnameList, boolean addDefaultProps ) {
        if( PropertyNameLists.isAllAttributes(pnameList) ) {
            Element allpropElm = new Element( "allprop", dnsp );
            contextElm.addContent( allpropElm );
        }
        else {
            Element propElm = new Element( "prop", dnsp );
            contextElm.addContent( propElm );
            if (addDefaultProps) {
                createDefaultPropertyElements( propElm, pnameList );
            }
            createPropertyElements( propElm, pnameList );
        }
    }
    
    /**
     * Add default property elements, needed for resource type determination,
     * to the specified context element.
     *
     * @param    contextElm          an Element
     */
    protected void createDefaultPropertyElements( Element contextElm, PropertyNameList pnameList ) {
        Set pnames = new HashSet();
        if (pnameList != null) {
            for (int i = 0; i < pnameList.getPropertyNames().length; i++) {
                PropertyName p = pnameList.getPropertyNames()[i];
                String s;
                if (p instanceof AttributeName && "DAV:".equals(((AttributeName)p).getNamespace())) {
                    s = p.getString();
                }
                else {
                    s = WebdavProperties.webdavNameForPropertyName(p);
                }
                if (s != null) {
                    pnames.add(s);
                }
            }
        }
        for (int i = 0; i < defaultProperties.length; i++) {
            String s = defaultProperties[i];
            if (!pnames.contains(defaultProperties[i])) {
                createDefaultPropertyElement(contextElm, defaultProperties[i]);
            }
        }
    }
    
    protected void createDefaultPropertyElement(Element contextElm, String name) {
        contextElm.addContent(new Element(name, dnsp));
    }
    
    /**
     * Create property elements in the specified context.
     *
     * @param    contextElm the context
     * @param    pnameList the wanted properties
     *
     */
    protected void createPropertyElements( Element contextElm, PropertyNameList pnameList ) {
        if( pnameList == null )
            return;
        
        PropertyName[] pnames;
        pnames = pnameList.getPropertyNames();
        for( int i = 0; i < pnames.length; i++ ) {
            PropertyName pname = pnames[i];
            Element propertyElm = createPropertyElement(pname);
            
            if( propertyElm == null ) {
                if( pname == PropertyName.IS_CHECKED_OUT ) {
                    if( !Arrays.asList(pnames).contains(PropertyName.CHECKED_OUT) ) {
                        String webdavname = WebdavProperties.webdavNameForPropertyName( PropertyName.CHECKED_OUT );
                        propertyElm = new Element( webdavname, dnsp );
                    }
                }
                else if( pname == PropertyName.CONTENT_CHARACTER_SET ) {
                    if( !Arrays.asList(pnames).contains(PropertyName.CONTENT_TYPE) ) {
                        String webdavname = WebdavProperties.webdavNameForPropertyName( PropertyName.CONTENT_TYPE );
                        propertyElm = new Element( webdavname, dnsp );
                    }
                }
                // TODO: prio=l, effort=2.0, descr=(handle non-WebDAV "dirty-property-list","is-dirty-content","is-stale-content","server-state","stale-property-list")
            }
            
            if( propertyElm != null )
                contextElm.addContent( propertyElm );
        }
    }
    
    /**
     * Create property element for specified PropertyName
     *
     * @param    pname a  PropertyName
     * @return   the property element
     */
    protected Element createPropertyElement(PropertyName pname) {
        Element propertyElm = null;
        String webdavname = WebdavProperties.webdavNameForPropertyName( pname );
        if( pname instanceof XPathAttributeName ) {
            XPathAttributeName attname = (XPathAttributeName)pname;
            Namespace xsvnsp = Namespace.getNamespace("xsv", "http://namespaces.softwareag.com/tamino/webdav");
            propertyElm = new Element( "xpath", xsvnsp );
            propertyElm.setAttribute( "name", attname.getString() );
            propertyElm.setAttribute( "namespace", attname.getNamespace() );
            propertyElm.addContent( new Text(attname.getXpath()) );
            propertyElm.setAttribute( "plain", "true" );
        }
        else if( pname instanceof AttributeName ) {
            AttributeName attname = (AttributeName)pname;
            propertyElm = new Element( attname.getString(), Namespace.getNamespace(attname.getNamespace()) );
        }
        else if( (pname instanceof NestedPropertyName) ) {
            // TODO: prio=l, effort=2.0, desc=(handle NestedPropertyName)
            // workaround: ignore pnmae.getNested for now
            propertyElm = createPropertyElement(((NestedPropertyName)pname).getName());
        }
        else if( webdavname != null ) {
            propertyElm = new Element( webdavname, dnsp );
        }
        return propertyElm;
    }
    
    /**
     * Create a privilege element for the specified Privilege in the specified context
     *
     * @param    contextElm          an Element
     * @param    privilege           a  Privilege
     */
    protected void createPrivilegeElement(Element contextElm, Privilege privilege) {
        Element privilegeElm = new Element("privilege", dnsp);
        Element pElm = new Element(privilege.location().lastSegment(), dnsp);
        privilegeElm.addContent(pElm);
        contextElm.addContent(privilegeElm);
    }
    
    /**
     * Create a href element for the specified resource in the specified context
     *
     * @param    contextElm          an Element
     * @param    resource            a  Resource
     */
    protected void createHrefElement( Element contextElm, Resource resource ) {
        createHrefElement( contextElm, resource.location().string() );
    }
    
    /**
     * Create a href element for the specified resource path in the specified context
     *
     * @param    contextElm          an Element
     * @param    resourcePath        a  String
     */
    protected void createHrefElement( Element contextElm, String resourcePath ) {
        Element hrefElm = new Element( "href", dnsp );
        hrefElm.addContent( new Text(resourcePath) );
        contextElm.addContent( hrefElm );
    }
    
    /**
     * Create a href elements for all the specified resources in the specified context
     *
     * @param    contextElm          an Element
     * @param    resourceList        a List of Resource instances
     */
    protected void createHrefElements( Element contextElm, List resourceList ) {
        if( resourceList != null ) {
            Iterator i = resourceList.iterator();
            while( i.hasNext() ) {
                Resource resource = (Resource)i.next();
                createHrefElement( contextElm, resource.location().string() );
            }
        }
    }
}

