/*
 * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/util/PropertyRetrieverImpl.java,v 1.45 2005/01/10 18:13:25 luetzkendorf Exp $
 * $Revision: 1.45 $
 * $Date: 2005/01/10 18:13:25 $
 *
 * ====================================================================
 *
 * 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;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.PropertyName;
import org.apache.slide.common.RequestedProperties;
import org.apache.slide.common.RequestedPropertiesImpl;
import org.apache.slide.common.RequestedProperty;
import org.apache.slide.common.RequestedPropertyImpl;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.UriPath;
import org.apache.slide.content.Content;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.NodeProperty.NamespaceCache;
import org.apache.slide.lock.Lock;
import org.apache.slide.search.RequestedResource;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.security.Security;
import org.apache.slide.structure.Structure;
import org.apache.slide.util.Configuration;
import org.apache.slide.util.XMLValue;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.util.resourcekind.AbstractResourceKind;
import org.apache.slide.webdav.util.resourcekind.ResourceKind;
import org.jdom.CDATA;
import org.jdom.Comment;
import org.jdom.Element;
import org.jdom.EntityRef;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.ProcessingInstruction;
import org.jdom.Text;


/**
 * This class is used to retrieve the properties of a resource as a
 * <code>&lt;propstat&gt;</code> element. The main code of this class
 * has been copied from the <code>PropFindMethod</code>. All methods
 * providing property information (<code>PropFindMethod</code>,
 * <code>ReportMethod</code>) should use this class.
 *
 * @version $Revision: 1.45 $
 *
 */
public class PropertyRetrieverImpl extends AbstractWebdavHelper implements PropertyRetriever, WebdavConstants, DeltavConstants, AclConstants, BindConstants {
    
    
    /**
     * The Content helper.
     */
    protected Content content = null;
    
    /**
     * The Lock helper used to access the locks.
     */
    protected Lock lock = null;
    
    /**
     * The Security helper.
     */
    protected Security security = null;
    
    /**
     * The Structure helper.
     */
    protected Structure structure = null;
    
    /**
     * The PropertyHelper used to access the (computed) properties.
     */
    protected PropertyHelper propertyHelper = null;
    
    /**
     * the webdavservlet configuration
     */
    protected WebdavServletConfig config = null;
    /**
     * Creates a PropertyRetriever from the given parameters.
     *
     * @param      token       the NamespaceAccessToken used to access the helpers.
     * @param      slideToken  the SlideToken used to retrieve the NodeRevisionDescriptor(s).
     */
    public PropertyRetrieverImpl(NamespaceAccessToken token, SlideToken slideToken, WebdavServletConfig sConf) {
        
        super(slideToken, token);
        this.structure = token.getStructureHelper();
        this.content = token.getContentHelper();
        this.security = token.getSecurityHelper();
        this.lock = token.getLockHelper();
        this.config = sConf;
        propertyHelper = PropertyHelper.getPropertyHelper(slideToken, token, sConf);
    }
    
    /** deprecated */
    //    public PropertyRetrieverImpl(NamespaceAccessToken token, SlideToken slideToken) {
    //        super(slideToken, token);
    //        this.structure = token.getStructureHelper();
    //        this.content = token.getContentHelper();
    //        this.security = token.getSecurityHelper();
    //        this.lock = token.getLockHelper();
//
    //        propertyHelper = PropertyHelper.getPropertyHelper(slideToken, token, null);
    //    }
    
    /**
     * Returns the PropertyHelper used to access the (computed) properties.
     *
     * @return     the PropertyHelper used to access the (computed) properties.
     */
    protected PropertyHelper getPropertyHelper() {
        return propertyHelper;
    }
    
    
    /**
     * Returns the requested properties of the last revision of the resource
     * identified by the given <code>uri</code> as list of <code>&lt;propstat&gt;</code>
     * JDOM Elements.
     *
     * @param      requestedProperties  the requested properties.
     * @param      uri                  the URI of the resource.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     * @param      extendedAllprop  indicates if the <code>DeltaV</code> specific
     *                                    properties should be included in case
     *                                    all properties are requested.
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     * @throws     JDOMException  if creating the JDOM Element fails.
     */
    public List getPropertiesOfObject(RequestedProperties requestedProperties, String uri, String servletContextPath, boolean extendedAllprop) throws SlideException, JDOMException  {
        
        NodeRevisionDescriptors revisionDescriptors = null;
        NodeRevisionDescriptor revisionDescriptor = null;
        
        //boolean isCollection = false;
        
        try {
            revisionDescriptors =
                content.retrieve(sToken, uri);
            
            try {
                
                revisionDescriptor = content.retrieve(sToken,
                                                      revisionDescriptors);
                //isCollection = WebdavUtils.isCollection(revisionDescriptor);
                
                
            } catch (RevisionDescriptorNotFoundException e) {
                
                // The object doesn't have any revision, we create a dummy
                // NodeRevisionDescriptor object
                //isCollection = true;
                revisionDescriptor = new NodeRevisionDescriptor(0);
                
                if (!Configuration.useBinding(nsaToken.getUri(sToken, uri).getStore())) {
                    revisionDescriptor.setName(UriPath.getLastSegment(uri));
                }
            }
            
        } catch (AccessDeniedException e) {
            if (revisionDescriptor == null) {
                revisionDescriptor = new NodeRevisionDescriptor(0);
            }
        }
        //      catch (Exception e) {
        //          // resp.setStatus(getErrorCode(e));
        //          throw new WebdavException(getErrorCode(e)); // abort the TA
        //      }
        
        return getPropertiesOfObject(requestedProperties, revisionDescriptors, revisionDescriptor, servletContextPath, extendedAllprop);
    }
    
    /**
     * Returns the requested properties of the resource identified by the given
     * <code>uri</code> and <code>revisionNumber</code> as list of
     * <code>&lt;propstat&gt;</code> JDOM Elements.
     *
     * @param      requestedProperties  the requested properties.
     * @param      uri                  the URI of the resource.
     * @param      revisionNumber       the revision number of the requested resource.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     * @param      extendedAllprop  indicates if the <code>DeltaV</code> specific
     *                                    properties should be included in case
     *                                    all properties are requested.
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     * @throws     JDOMException  if creating the JDOM Element fails.
     */
    public List getPropertiesOfObject(RequestedProperties requestedProperties, String uri, NodeRevisionNumber revisionNumber, String servletContextPath, boolean extendedAllprop) throws SlideException, JDOMException {
        
        NodeRevisionDescriptors revisionDescriptors = content.retrieve(sToken, uri);
        NodeRevisionDescriptor revisionDescriptor = content.retrieve(sToken,
                                                                     revisionDescriptors,
                                                                     revisionNumber);
        return getPropertiesOfObject(requestedProperties, revisionDescriptors, revisionDescriptor, servletContextPath, extendedAllprop);
    }
    
    /**
     * Returns the requested properties of the resource identified by the given
     * <code>uri</code> and <code>revisionNumber</code> as list of
     * <code>&lt;propstat&gt;</code> JDOM Elements.
     *
     * @param      requestedProperties  the requested properties.
     * @param      revisionDescriptors    the NodeRevisionDescriptors of the resource.
     * @param      revisionDescriptor     the NodeRevisionDescriptor of the resource.
     * @param      contextPath           a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath           a String, the result of HttpRequest.getServletPath()
     * @param      extendedAllprop  indicates if the <code>DeltaV</code> specific
     *                                    properties should be included in case
     *                                    all properties are requested.
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     * @throws     JDOMException  if creating the JDOM Element fails.
     */
    public List getPropertiesOfObject(RequestedProperties requestedProperties, 
            NodeRevisionDescriptors revisionDescriptors, 
            NodeRevisionDescriptor revisionDescriptor, 
            String servletContextPath, boolean extendedAllprop) 
    throws SlideException, JDOMException 
    {
        
        ComputedPropertyProvider propertyProvider = new ComputedPropertyProvider(
                nsaToken,
                sToken,
                propertyHelper,
                servletContextPath);
        
        return getPropertiesOfObject(requestedProperties,
                                     new ResourceWithProvidedProperties(
                                             revisionDescriptors,
                                             revisionDescriptor,
                                             propertyProvider),
                                     servletContextPath,
                                     extendedAllprop);
    }
    
    /**
     * Returns the requested properties of the <code><requestedResource/code>
     * as list of <code>&lt;propstat&gt;</code> JDOM Elements.
     *
     * @param      requestedProperties  the requested properties.
     * @param      requestedResource    the resource for which to return the properties.
     * @param      contextPath           a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath           a String, the result of HttpRequest.getServletPath()
     * @param      extendedAllprop  indicates if the <code>DeltaV</code> specific
     *                                    properties should be included in case
     *                                    all properties are requested.
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     * @throws     JDOMException  if creating the JDOM Element fails.
     */
    public List getPropertiesOfObject(RequestedProperties requestedProperties, 
            RequestedResource requestedResource, String servletContextPath, 
            boolean extendedAllprop) throws SlideException, JDOMException {
        
        List elementList = new ArrayList();
        
        if (requestedProperties.isAllProp()) {
            elementList = getAllPropertiesOfObject(
                    requestedResource, servletContextPath, extendedAllprop);
        }
        else {
            elementList = getRequestedPropertiesOfObject(
                    requestedProperties, requestedResource, servletContextPath);
        }
        return elementList;
    }
    
    /**
     * Returns the requested properties of the <code><requestedResource/code>
     * as list of <code>&lt;propstat&gt;</code> JDOM Elements.
     *
     * @param      requestedProperties  the requested properties.
     * @param      requestedResource    the resource for which to return the properties.
     * @param      contextPath           a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath           a String, the result of HttpRequest.getServletPath()
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     */
    protected List getRequestedPropertiesOfObject(RequestedProperties requestedProperties, RequestedResource requestedResource, String servletContextPath) throws SlideException {
        
        List elementList = new ArrayList();
        Iterator propertyIterator = requestedProperties.getRequestedProperties();
        
        Element propstat = new Element(E_PROPSTAT, NamespaceCache.DEFAULT_NAMESPACE);
        Element prop = new Element(E_PROP, NamespaceCache.DEFAULT_NAMESPACE);
        propstat.addContent(prop);
        Element propertyElement = null;
        boolean anyPropertyFound = false;
        
        // maps the code of the error (that occurred when retrieving the property)
        // to the List of RequestedProperty that caused that error
        Map erroneousPropertiesMap = new HashMap();
        
        String status = "HTTP/1.1 " + WebdavStatus.SC_OK
                        + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK);
        
        while (propertyIterator.hasNext()) {
            
            RequestedProperty property = (RequestedProperty)propertyIterator.next();
            NodeProperty currentProperty = null;
            Integer errorCode = null;
            try {
                currentProperty = requestedResource.getProperty(property.getPropertyName());
                if (currentProperty == null) {
                    errorCode = new Integer(WebdavStatus.SC_NOT_FOUND);
                }
            }
            catch (AccessDeniedException e) {
                errorCode = new Integer(WebdavStatus.SC_FORBIDDEN);
            }
            
            if (errorCode != null) {
                List erroneousPropertiesList = (List)erroneousPropertiesMap.get(errorCode);
                if (erroneousPropertiesList == null) {
                    erroneousPropertiesList = new ArrayList();
                    erroneousPropertiesMap.put(errorCode, erroneousPropertiesList);
                }
                erroneousPropertiesList.add(property.getPropertyName());
            }
            else {
                propertyElement = getPropertyElement(currentProperty, servletContextPath);
                if (propertyElement != null) {
                    anyPropertyFound = true;
                    prop.addContent(propertyElement);
                }
            }
        }
        
        if (anyPropertyFound) {
            Element statusElement = new Element(E_STATUS, NamespaceCache.DEFAULT_NAMESPACE);
            statusElement.setText(status);
            propstat.addContent(statusElement);
            elementList.add(propstat);
        }
        
        elementList.addAll(getPropstatForErroneousProperties(erroneousPropertiesMap, servletContextPath));
        
        return elementList;
    }
    
    
    /**
     * Returns all properties of the <code><requestedResource/code>
     * as list of <code>&lt;propstat&gt;</code> JDOM Elements.
     *
     * @param      requestedResource    the resource for which to return the properties.
     * @param      contextPath           a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath           a String, the result of HttpRequest.getServletPath()
     * @param      extendedAllprop  indicates if the <code>DeltaV</code> specific
     *                                    properties should be included in case
     *                                    all properties are requested.
     *
     * @return     the requested properties as list of <code>&lt;propstat&gt;</code>
     *             JDOM Element.
     *
     * @throws     SlideException
     */
    protected List getAllPropertiesOfObject(RequestedResource requestedResource, String servletContextPath, boolean extendedAllprop) throws SlideException {
        
        List elementList = new ArrayList();
        Element propstat = new Element(E_PROPSTAT, NamespaceCache.DEFAULT_NAMESPACE);
        elementList.add(propstat);
        Element prop = new Element(E_PROP, NamespaceCache.DEFAULT_NAMESPACE);
        propstat.addContent(prop);
        Element propertyElement = null;
        
        // maps the code of the error (that occurred when retrieving the property)
        // to the List of RequestedProperty that caused that error
        Map erroneousPropertiesMap = new HashMap();
        
        
        String status = "HTTP/1.1 " + WebdavStatus.SC_OK 
                + " " + WebdavStatus.getStatusText(WebdavStatus.SC_OK);
        
        Iterator propertyNameIterator = requestedResource.getAllPropertiesNames();
        while (propertyNameIterator.hasNext()) {
            
            PropertyName currentPropertyName = (PropertyName) propertyNameIterator.next();
            if (currentPropertyName != null) {
                
                if ( !extendedAllprop && S_DAV.equals(currentPropertyName.getNamespace()) && (
                        DeltavConstants.DELTAV_PROPERTY_LIST.contains(currentPropertyName.getName()) ||
                            BindConstants.BIND_PROPERTY_LIST.contains(currentPropertyName.getName()) ||
                            AclConstants.ACL_PROPERTY_LIST.contains(currentPropertyName.getName())
                    )
                   ) {
                    // skip properties from specification extensions
                    continue;
                }
                
                NodeProperty currentProperty = null;
                Integer errorCode = null;
                try {
                    currentProperty = requestedResource.getProperty(currentPropertyName);
                    if (currentProperty == null) {
                        errorCode = new Integer(WebdavStatus.SC_NOT_FOUND);
                    }
                }
                catch (AccessDeniedException e) {
                    errorCode = new Integer(WebdavStatus.SC_FORBIDDEN);
                }
                
                if (errorCode != null) {
                    List erroneousPropertiesList = (List)erroneousPropertiesMap.get(errorCode);
                    if (erroneousPropertiesList == null) {
                        erroneousPropertiesList = new ArrayList();
                        erroneousPropertiesMap.put(errorCode, erroneousPropertiesList);
                    }
                    erroneousPropertiesList.add(currentPropertyName);
                }
                else {
                    propertyElement = getPropertyElement(currentProperty, servletContextPath);
                    if (propertyElement != null) {prop.addContent(propertyElement);}
                }
            }
        }
        
        
        Element statusElement = new Element(E_STATUS, NamespaceCache.DEFAULT_NAMESPACE);
        statusElement.setText(status);
        propstat.addContent(statusElement);
        
        elementList.addAll(getPropstatForErroneousProperties(erroneousPropertiesMap, servletContextPath));
        
        return elementList;
    }

    /**
     * Returns the requested properties of the resource identified by the given
     * <code>uri</code> and <code>revisionNumber</code> as list of
     * <code>&lt;propstat&gt;</code> JDOM Elements, but representing the
     * redirect reference as a file resource.  This method is essentially the
     * same as <code>getPropertiesOfObject</code>, but the generated
     * <code>RequestedResource</code> is replaced.  This method should only
     * be called for redirect references when the user-agent doesn't support
     * redirect references and requires this workaround.
     *
     * @param      requestedProperties    the requested properties.
     * @param      revisionDescriptors    the NodeRevisionDescriptors of the
     *                                    resource.
     * @param      revisionDescriptor     the NodeRevisionDescriptor of the
     *                                    resource.
     * @param      contextPath            a String, the result of
     *                                    HttpRequest.getContextPath()
     * @param      servletPath            a String, the result of
     *                                    HttpRequest.getServletPath()     
     * @param      extendedAllprop        indicates if the <code>DeltaV</code>
     *                                    specific properties should be included
     *                                    in case all properties are requested.
     *
     * @return     the requested properties as list of
     *             <code>&lt;propstat&gt;</code> JDOM Element.
     *
     * @throws     SlideException
     *
     * @see        #getPropertiesOfObject(RequestedProperties,
     *                     NodeRevisionDescriptors, NodeRevisionDescriptor,
     *                     String, String, boolean)
     */
    public List getWorkaroundPropertiesOfObject
        (RequestedProperties requestedProperties,
        NodeRevisionDescriptors revisionDescriptors,
        NodeRevisionDescriptor revisionDescriptor, String servletContextPath,
        boolean extendedAllprop)
        throws SlideException, JDOMException
    {
        ComputedPropertyProvider propertyProvider
                = new ComputedPropertyProvider(nsaToken, sToken, propertyHelper,
                        servletContextPath);
        return getPropertiesOfObject(requestedProperties,
                new RedirectRefWorkaroundWithProvidedProperties(
                        revisionDescriptors, revisionDescriptor,
                        propertyProvider),
                servletContextPath, extendedAllprop);
    }

    /**
     * Returns a list of <code>&lt;propstat&gt;</code> JDOM Elements for the properties
     * that could not be retrieved due to an error.
     *
     * @param      erroneousPropertiesMap  maps the (Integer) code of the error (that occurred
     *                                     when retrieving the property) to the List of
     *                                     PropertyName that caused that error.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     *
     * @return     A List of <code>&lt;propstat&gt;</code> JDOM Elements for the erroneous properties.
     */
    protected List getPropstatForErroneousProperties(Map erroneousPropertiesMap, String servletContextPath) {
        
        List elementList = new ArrayList();
        Iterator iterator = erroneousPropertiesMap.keySet().iterator();
        while (iterator.hasNext()) {
            
            Integer errorCode = (Integer)iterator.next();
            List erroneousPropertiesList = (List)erroneousPropertiesMap.get(errorCode);
            Element propstat = new Element(E_PROPSTAT, NamespaceCache.DEFAULT_NAMESPACE);
            elementList.add(propstat);
            Element prop = new Element(E_PROP, NamespaceCache.DEFAULT_NAMESPACE);
            propstat.addContent(prop);
            
            String status = "HTTP/1.1 " + errorCode.intValue() + " " + 
                        WebdavStatus.getStatusText(errorCode.intValue());
            
            Iterator propertyIterator = erroneousPropertiesList.iterator();
            while (propertyIterator.hasNext()) {
                
                PropertyName erroneousProperty =
                    (PropertyName) propertyIterator.next();
                Element propertyElement = getPropertyElement(erroneousProperty.getNamespace(),
                                                             erroneousProperty.getName(),
                                                             null,
                                                             servletContextPath);
                if (propertyElement != null) {prop.addContent(propertyElement);}
            }
            
            Element statusElement = new Element(E_STATUS, NamespaceCache.DEFAULT_NAMESPACE);
            statusElement.setText(status);
            propstat.addContent(statusElement);
        }
        
        return elementList;
    }
    
    
    
    /**
     * Returns the JDOM Element for the given <code>property</code>.
     *
     * @param      property             the property for which to return the Element.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     *
     * @return     the JDOM Element for the given <code>property</code>.
     */
    private Element getPropertyElement(NodeProperty property, String servletContextPath) {
        if (property != null) {
            return getPropertyElement(property.getNamespace(),
                                      property.getName(),
                                      property.getValue(),
                                      servletContextPath);
        }
        return null;
    }
    
    /**
     * Returns the JDOM Element for the given <code>property</code>.
     *
     * @param      namespaceString      the property namespace.
     * @param      propertyName         the property Name.
     * @param      propertyValue        the property Value.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     *
     * @return     the JDOM Element for the given <code>property</code>.
     */
    private Element getPropertyElement(String namespaceString,
                                       String propertyName,
                                       Object propertyValue,
                                       String servletContextPath) {
        Element property = null;
        Namespace namespace = Namespace.NO_NAMESPACE;
        Namespace valueDefaultNamespace = null;
        if (namespaceString != null) {
            namespace = NamespaceCache.getNamespace(namespaceString);
            if (NamespaceCache.DEFAULT_URI.equals(namespace.getURI())) {
                // use the DAV: as the default namespace
                // for XML values of DAV: properties
                valueDefaultNamespace = namespace;
            }
        }
        
        property = new Element(propertyName, namespace);
        if ( (propertyValue != null) && (propertyValue.toString().length() > 0)) {
            if( propertyValue.toString().indexOf('<') >= 0 ) {
                try {
                    XMLValue xmlValue = new XMLValue(propertyValue.toString(), valueDefaultNamespace);
                    if (AbstractResourceKind.isLiveProperty(propertyName)) {
                        convertHrefValueToAbsoluteURL (xmlValue, servletContextPath, config);
                    }
                    Iterator iterator = xmlValue.iterator();
                    while (iterator.hasNext()) {
                        Object o = iterator.next();
                        if( o instanceof Element )
                            property.addContent((Element)o);
                        else if( o instanceof Text )
                            property.addContent((Text)o);
                        else if( o instanceof Comment )
                            property.addContent((Comment)o);
                        else if( o instanceof ProcessingInstruction )
                            property.addContent((ProcessingInstruction)o);
                        else if( o instanceof CDATA )
                            property.addContent((CDATA)o);
                        else if( o instanceof EntityRef )
                            property.addContent((EntityRef)o);
                    }
                }
                catch (JDOMException e) {
                    property.setText(propertyValue.toString());
                }
            }
            else {
                property.setText(propertyValue.toString());
            }
        }
        return property;
    }
    
    
    
    
    /**
     * If the given <code>xmlValue</code> contains <code>&lt;href&gt;</code> elements,
     * the relative URI is converted to an absolute one.
     *
     * @param      xmlValue             the XMLValue that might contain <code>&lt;href&gt;</code>
     *                                  values to convert.
     * @param      contextPath          a  String , the result of HttpRequest.getContextPath()
     * @param      servletPath          a String, the result of HttpRequest.getServletPath()
     */
    protected static void convertHrefValueToAbsoluteURL (XMLValue xmlValue,
                                                         String servletContextPath,
                                                         WebdavServletConfig config) {
        if (xmlValue != null) {
            Iterator iterator = xmlValue.iterator();
            Element element = null;
            while (iterator.hasNext()) {
                Object o = iterator.next();
                if( o instanceof Element ) {
                    element = (Element)o;
                    convertHrefValueToAbsoluteURL(element, servletContextPath, config);
                }
            }
        }
    }
    
    private static void convertHrefValueToAbsoluteURL (Element element,
                                                       String servletContextPath,
                                                       WebdavServletConfig config) {
        if (element.getChildren().size() > 0) {
            Iterator i = element.getChildren().iterator();
            while (i.hasNext()) {
                Element child = (Element)i.next();
                // hrefs for owner and active locks have to be left untouched (issue 32436)
                if (!(element.getName().equals("activelock") && child.getName().equals("owner"))) {
                    convertHrefValueToAbsoluteURL(child, servletContextPath, config);
                }
            }
        }
        if ( E_HREF.equals(element.getName()) && (element.getText() != null) ) {
            if ( !PropertyHelper.isAbsoluteURL(servletContextPath, element.getText()) ) {
                
                element.setText(WebdavUtils.getAbsolutePath (element.getText(),
                        servletContextPath, config));
            }
        }
    }
    
    /**
     * Discover what properties exist or are supported by a resource.
     * @param    resourcePath          a  String
     * @param    liveOnly              only live properties
     * @return   a RequestedProperties
     * @throws   SlideException
     */
    public RequestedProperties getAllPropertyNames(String resourcePath, boolean liveOnly) throws SlideException {
        NodeRevisionDescriptor nrd =
            content.retrieve(sToken, content.retrieve(sToken, resourcePath));
        ResourceKind resourceKind = AbstractResourceKind.determineResourceKind(nsaToken, resourcePath, nrd);
        RequestedPropertiesImpl result = (RequestedPropertiesImpl)getAllPropertyNames(resourceKind);
        
        if (!liveOnly) {
            // add this resource's dead properties
            Enumeration props = nrd.enumerateProperties();
            while (props.hasMoreElements()) {
                NodeProperty np = (NodeProperty)props.nextElement();
                if (!result.contains(np)) {
                    result.addProperty(new RequestedPropertyImpl(np.getName(), np.getNamespace()));
                }
            }
        }
        return result;
    }
    
    /**
     * Discover what properties are supported by the specified resourcetype.
     *
     * @param    resourceKind        a  ResourceKind
     *
     * @return   a RequestedProperties
     *
     * @throws   SlideException
     *
     */
    public RequestedProperties getAllPropertyNames(ResourceKind resourceKind) throws SlideException {
        RequestedPropertiesImpl result = new RequestedPropertiesImpl();
        Iterator liveprops = resourceKind.getSupportedLiveProperties().iterator();
        while (liveprops.hasNext()) {
            result.addProperty(new RequestedPropertyImpl((String)liveprops.next(), DNSP.getURI()));
        }
        return result;
    }
    
}


