/*
 * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/properties/LockDiscoveryProperty.java,v 1.3 2005/01/03 15:04:44 luetzkendorf Exp $
 * $Revision: 1.3 $
 * $Date: 2005/01/03 15:04:44 $
 *
 * ====================================================================
 *
 * 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.util.properties;

import java.io.StringReader;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;

import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.PropertyName;
import org.apache.slide.common.SlideException;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.lock.Lock;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.XMLValue;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.method.LockMethod;
import org.apache.slide.webdav.util.AclConstants;
import org.apache.slide.webdav.util.WebdavConstants;
import org.apache.slide.webdav.util.WebdavContext;
import org.apache.slide.webdav.util.WebdavUtils;
import org.apache.slide.webdav.util.resourcekind.ResourceKind;
import org.jdom.CDATA;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;


/**
 * Computes the WebDAV <code>lockdiscovery</code> property.
 */
public class LockDiscoveryProperty extends AbstractComputedProperty
{
    public PropertyName getPropertyName() {
        return AclConstants.PN_LOCKDISCOVERY;
    }
    
    public Object computeValue(NamespaceAccessToken nsaToken,
            NodeRevisionDescriptors revisionDescriptors,
            NodeRevisionDescriptor revisionDescriptor, ResourceKind resourceKind, WebdavContext context)
            throws SlideException
    {
        XMLValue xmlValue = new XMLValue();
        Lock lock = nsaToken.getLockHelper();
        NodeLock objectLockToken = null;
        Enumeration lockTokens = lock.enumerateLocks(
                context.getSlideToken(), revisionDescriptors.getUri(), true);
        Set addedLockIDs = new HashSet();
        while (lockTokens.hasMoreElements()) {
            objectLockToken = (NodeLock) lockTokens.nextElement();
            if (revisionDescriptors.getUri().equals(objectLockToken.getObjectUri()) ||
                objectLockToken.isInheritable()
               ) {
                
                if (!addedLockIDs.contains(objectLockToken.getLockId())) {
                    Element activelock = createActiveLockElement(objectLockToken,
                            nsaToken, context.getServletConfig(), 
                            context.getContextPath());
                    if (activelock != null) {
                        xmlValue.add(activelock);
                        addedLockIDs.add(objectLockToken.getLockId());
                    }
                }
            }
        }
        
        return xmlValue;
    }

    
    /**
     * Returns the <code>&lt;activelock&gt;</code> element to be used as child
     * the <code>&lt;lockdiscovery&gt;</code> property.
     *
     * @param    objectLockToken     the NodeLock for which to compute the value
     *                               of the <code>&lt;activelock&gt;</code>.
     * @param    servletPath         a String, the result of HttpRequest.getServletPath()
     * @param    contextPath         a  String , the result of HttpRequest.getContextPath()
     *
     * @return   the <code>&lt;activelock&gt;</code> element.
     *
     */
    public static Element createActiveLockElement(NodeLock objectLockToken, 
            NamespaceAccessToken nsaToken, WebdavServletConfig sConf, 
            String slideContextPath) {
        
        Element activelock = null;
        
        if (objectLockToken != null) {
            
            activelock = new Element(WebdavConstants.E_ACTIVELOCK, DNSP);
            Element locktype = new Element(WebdavConstants.E_LOCKTYPE, DNSP);
            activelock.addContent(locktype);
            if (objectLockToken.isLocal()) {
                Element transaction = new Element(WebdavConstants.E_TRANSACTION, DNSP);
                Element groupoperation = new Element(WebdavConstants.E_GROUPOPERATION, DNSP);
                transaction.addContent(groupoperation);
                locktype.addContent(transaction);
            } else {
                Element write = new Element(WebdavConstants.E_WRITE, DNSP);
                locktype.addContent(write);
            }
            Element lockscope = new Element(WebdavConstants.E_LOCKSCOPE, DNSP);
            activelock.addContent(lockscope);
            Element lockscopeValue = null;
            if (objectLockToken.isExclusive()) {
                lockscopeValue = new Element(WebdavConstants.E_EXCLUSIVE, DNSP);
            } else if (objectLockToken.isShared()) {
                lockscopeValue = new Element(WebdavConstants.E_SHARED, DNSP);
            } else if (objectLockToken.isLocal()) {
                lockscopeValue = new Element(WebdavConstants.E_LOCAL, DNSP);
            }
            lockscope.addContent(lockscopeValue);
            Element depth = new Element(WebdavConstants.E_DEPTH, DNSP);
            activelock.addContent(depth);
            if (objectLockToken.isInheritable()) {
                depth.setText("infinity");
            } else {
                depth.setText("0");
            }
            Element owner = new Element(WebdavConstants.E_OWNER, DNSP);
            activelock.addContent(owner);
            /* We need to write this as data (i.e. a CDATA section) because
             * we don't know what the subjectUri (i.e. username) might
             * contain. The webdav RFC leaves us completely free to
             * put whatever we want inside the owner element.
             */
            if( objectLockToken.getOwnerInfo() != null &&
               !objectLockToken.getOwnerInfo().equals(LockMethod.DEFAULT_LOCK_OWNER) ) {
                
                // try to parse; TODO reuse SAXBuilder
                try {
                    Document d =
                        new SAXBuilder().build( new StringReader(objectLockToken.getOwnerInfo()) );
                    owner.addContent(d.detachRootElement());
                }
                catch( Throwable e ) {
                    owner.addContent(new CDATA(objectLockToken.getOwnerInfo()));
                }
            }
            
            Element timeout = new Element(WebdavConstants.E_TIMEOUT, DNSP);
            activelock.addContent(timeout);
            long seconds = (new Long((objectLockToken.getExpirationDate().getTime()
                                          - (new Date()).getTime())/1000)).longValue();
            if (seconds > 0.75 * Integer.MAX_VALUE) {
                timeout.setText("Infinite");
            }
            else {
                timeout.setText("Second-"+seconds);
            }
            Element locktoken = new Element(WebdavConstants.E_LOCKTOKEN, DNSP);
            activelock.addContent(locktoken);
            Element href = new Element(WebdavConstants.E_HREF, DNSP);
            locktoken.addContent(href);
            href.setText(WebdavConstants.S_LOCK_TOKEN+objectLockToken.getLockId());
            
            // As proposed on February 08, 2003 by Lisa Dusseault in w3c-dist-auth-request@w3.org
            // >>>>>>>>>> start principal-URL >>>>>>>>>>>>
            if( sConf.lockdiscoveryIncludesPrincipalURL() ) {
                Element principalUrl = new Element( AclConstants.E_PRINCIPAL_URL, DNSP);
                activelock.addContent(principalUrl);
                SubjectNode snode =
                    SubjectNode.getSubjectNode(objectLockToken.getSubjectUri());
                String subjectUri = objectLockToken.getSubjectUri();
                if (snode.equals(SubjectNode.UNAUTHENTICATED)) {
                    String userspath = nsaToken.getNamespaceConfig().getUsersPath();
                    subjectUri = userspath+"/"+snode.toString();
                }
                Element puhref = new Element(WebdavConstants.E_HREF, DNSP);
                principalUrl.addContent(puhref);
                
                puhref.setText(
                    WebdavUtils.getAbsolutePath(subjectUri, slideContextPath, sConf));
            }
            // <<<<<<<<<< end principal-URL <<<<<<<<<<<<<<
        }
        return activelock;
    }
}
