/*
 * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/ReportMethod.java,v 1.74 2005/02/25 17:03:53 luetzkendorf Exp $
 * $Revision: 1.74 $
 * $Date: 2005/02/25 17:03:53 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.webdav.method;

import java.io.IOException;

import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.event.EventDispatcher;
import org.apache.slide.webdav.WebdavException;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.event.WebdavEvent;
import org.apache.slide.webdav.method.report.AbstractReport;
import org.apache.slide.webdav.method.report.AclPrincipalPropSetReport;
import org.apache.slide.webdav.method.report.ExpandPropertyReport;
import org.apache.slide.webdav.method.report.LocateByHistoryReport;
import org.apache.slide.webdav.method.report.PrincipalMatchReport;
import org.apache.slide.webdav.method.report.PrincipalPropertySearchReport;
import org.apache.slide.webdav.method.report.PrincipalSearchPropertySetReport;
import org.apache.slide.webdav.method.report.VersionTreeReport;
import org.apache.slide.webdav.util.AclConstants;
import org.apache.slide.webdav.util.DeltavConstants;
import org.apache.slide.webdav.util.PreconditionViolationException;
import org.apache.slide.webdav.util.WebdavStatus;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.output.XMLOutputter;


/**
 * REPORT method.
 *
 */
public class ReportMethod extends AbstractWebdavMethod implements DeltavConstants, AclConstants,
        ReadMethod, FineGrainedLockingMethod {
    
    private String resourcePath;
    private AbstractReport reportWorker;
    private String servletPath;
    
    /**
     * Constructor.
     *
     * @param token     the token for accessing the namespace
     * @param config    configuration of the WebDAV servlet
     */
    public ReportMethod(NamespaceAccessToken token, WebdavServletConfig config) {
        super(token, config);
    }
    
    /**
     * @see org.apache.slide.webdav.method.FineGrainedLockingMethod#acquireFineGrainLocks()
     */
    public void acquireFineGrainLocks() {
        acquireStandardLocks(resourcePath);
        // read lock history folder
        acquireHistoryLocks(resourcePath);
    }

    /**
     * Parse WebDAV XML request body.
     *
     * @exception WebdavException
     */
    protected void parseRequest() throws WebdavException {
        Element reportElm;
        String reportName;
        
        resourcePath = requestUri;
        if (resourcePath == null) {
            resourcePath = "/";
        }
        servletPath = req.getServletPath();
                
        try{
            reportElm = parseRequestContent().getRootElement();
            reportName = reportElm.getName();
            reportWorker = getExternalReportWorker(reportName);
            
            if (reportWorker == null) {
                if (R_EXPAND_PROPERTY.equals(reportName)) {
                    reportWorker = new ExpandPropertyReport(slideToken, token, config, getSlideContextPath());
            }
                else if (R_VERSION_TREE.equals(reportName)) {
                    reportWorker = new VersionTreeReport(slideToken, token, config, getSlideContextPath());
                ((VersionTreeReport)reportWorker).setVersioningHelper(versioningHelper);
            }
                else if (R_LOCATE_BY_HISTORY.equals(reportName)) {
                    reportWorker = new LocateByHistoryReport(slideToken, token, config, getSlideContextPath());
            }
                else if (R_ACL_PRINCIPAL_PROP_SET.equals(reportName)) {
                    reportWorker = new AclPrincipalPropSetReport(slideToken, token, config, getSlideContextPath());
            }
                else if (R_PRINCIPAL_MATCH.equals(reportName)) {
                    reportWorker = new PrincipalMatchReport(slideToken, token, config, getSlideContextPath());
            }
                else if (R_PRINCIPAL_PROPERTY_SEARCH.equals(reportName)) {
                    reportWorker = new PrincipalPropertySearchReport(slideToken, token, config, getSlideContextPath());
            }
                else if (R_PRINCIPAL_SEARCH_PROPERTY_SET.equals(reportName)) {
                    reportWorker = new PrincipalSearchPropertySetReport(slideToken, token, config, getSlideContextPath());
            }
            }
            
            if (reportWorker != null) {
                reportWorker.init(resourcePath, reportElm);
            }
            else {
                int statusCode = WebdavStatus.SC_BAD_REQUEST;
                sendError( statusCode, new Exception("Unknown report "+reportName) );
                throw new WebdavException( statusCode );
            }
        }
        catch (IOException e) {  // TODO: merge exception handling into jdom access methods
            int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
            sendError( statusCode, e );
            throw new WebdavException( statusCode );
        }
        catch (JDOMException e) {  // TODO: merge exception handling into jdom access methods
            int statusCode = WebdavStatus.SC_BAD_REQUEST;
            sendError( statusCode, e );
            throw new WebdavException( statusCode );
        }
        catch (PreconditionViolationException e) {  // TODO: merge exception handling into jdom access methods
            try {
                sendPreconditionViolation(e);
                throw e;
            } catch (IOException x) {}
            throw new WebdavException(e.getStatusCode());
        }
    }
    
    private AbstractReport getExternalReportWorker(String reportName) throws JDOMException, WebdavException {
        AbstractReport result = null;
        String clsName = config.getExternalReport(reportName);
        if (clsName != null) {
                try {
                    Class cls = Class.forName(clsName);
                    Class[] parmtypes = {SlideToken.class,
                            NamespaceAccessToken.class,
                            WebdavServletConfig.class,
                            String.class,
                            String.class
                    };
                    Object[] initargs = {slideToken,
                            token,
                            config,
                            servletPath,
                            getSlideContextPath()
                    };
                    result =
                        (AbstractReport)cls.getConstructor(parmtypes).newInstance(initargs);
                }
                catch( Exception e ) {
                    int statusCode = WebdavStatus.SC_BAD_REQUEST;
                sendError( statusCode, new Exception("Cannot create "+reportName+" report worker "+clsName+" ["+e+"]") );
                    throw new WebdavException( statusCode );
                }
            }
        return result;
    }
    
    /**
     * Execute the request.
     *
     * @exception WebdavException
     */
    protected void executeRequest() throws WebdavException, IOException {
        
        // check lock-null resources
        try {
            if (isLockNull(resourcePath)) {
                int statusCode = WebdavStatus.SC_NOT_FOUND;
                sendError( statusCode, "lock-null resource", new Object[]{resourcePath} );
                throw new WebdavException( statusCode );
            }
        }
        catch (ServiceAccessException e) {
            int statusCode = getErrorCode( e );
            sendError( statusCode, e );
            throw new WebdavException( statusCode );
        }
        
        try {
            if ( WebdavEvent.REPORT.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.REPORT, new WebdavEvent(this));

            reportWorker.checkPreconditions(resourcePath, getDepth());
            Element rootElm;
            if (reportWorker instanceof PrincipalSearchPropertySetReport) {
                rootElm = new Element(E_PRINCIPAL_SEARCH_PROPERTY_SET, DNSP);
                reportWorker.execute(resourcePath, rootElm, getDepth());
                resp.setStatus(WebdavStatus.SC_OK);
            }
            else {
                rootElm = new Element(E_MULTISTATUS, DNSP);
                reportWorker.execute(resourcePath, rootElm, getDepth());
                resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
            }
            org.jdom.output.Format format = org.jdom.output.Format.getPrettyFormat();
            format.setIndent(XML_RESPONSE_INDENT);
            new XMLOutputter(format).
                output(new Document(rootElm), resp.getWriter());
        }
        catch (PreconditionViolationException e) {
            sendPreconditionViolation(e);
            throw e;
        }
        catch (Exception e) {
            int statusCode = getErrorCode( e );
            sendError( statusCode, e );
            throw new WebdavException( statusCode );
        }
    }
    
    /**
     * Returns the value of the <code>Depth</code> request header.
     *
     * @return     the value of the <code>Depth</code> request header.
     */
    private int getDepth() throws WebdavException {
        int depth = requestHeaders.getDepth(0);
        
        // limit tree browsing a bit
        if (depth > config.getDepthLimit()) {
            depth = config.getDepthLimit();
        }
        return depth;
    }
}



