org.apache.webdav.lib.WebdavResource.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.webdav.lib.WebdavResource.java

Source

/*
 * $Header: /home/cvspublic/jakarta-slide/webdavclient/clientlib/src/java/org/apache/webdav/lib/WebdavResource.java,v 1.36 2005/03/17 07:20:59 masonjm Exp $
 * $Revision: 1.36 $
 * $Date: 2005/03/17 07:20:59 $
 *
 * ====================================================================
 *
 * 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.webdav.lib;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpURL;
import org.apache.commons.httpclient.HttpsURL;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.util.URIUtil;

import org.apache.webdav.lib.methods.*;
import org.apache.webdav.lib.properties.AclProperty;
import org.apache.webdav.lib.properties.LockDiscoveryProperty;
import org.apache.webdav.lib.properties.PrincipalCollectionSetProperty;
import org.apache.webdav.lib.properties.ResourceTypeProperty;
import org.apache.webdav.lib.util.DOMUtils;
import org.apache.webdav.lib.util.WebdavStatus;

/**
 * The class <code>WebdavResource</code> is an abstract representation
 * for WebDAV resource.<p>
 *
 * <pre>
 * A functional comparison of WebdavResource and JDK(It's different a lot).
 * ----------------------------------+-------------------------------------
 *   File class (JDK 1.3.x)          |  WebdavResource class
 * ----------------------------------+-------------------------------------
 *   File(String)                    |  WebdavResource(HttpURL)
 *   File(String, String)            |  X (need to escape)
 *   File(File, String)              |  WebdavResource(HttpURL, String)
 *   getName()                       |  getName()
 *   getParent()                     |  *see getHttpURL() and HttpURL
 *   getParentFile()                 |  X (not yet)
 *   getPath()                       |  getPath()
 *   isAbsolute()                    |  X
 *   getAbsolutePath()               |
 *   getAbsoluteFile()               |  X
 *   getCanonicalPath()              |
 *   getCanonicalFile()              |  X
 *   toURL()                         |  *see HttpURL
 *   canRead()                       |
 *   !canWrite()                     |  !isLocked()
 *   exists()                        |  exists()
 *   isDirectory()                   |  isCollection()
 *   isFile()                        |  !isCollection()
 *   isHidden()                      |  getIsHidden()
 *   lastModified()                  |  getGetLastModified()
 *   length()                        |  getGetContentLength()
 *   createNewFile()                 |  putMethod(String)
 *   delete()                        |  deleteMethod()
 *   deleteOnExit()                  |  X
 *   list()                          |  list()
 *   list(FilenameFilter)            |  X
 *   listFiles()                     |  listWebdavResources()
 *   listFiles(FilenameFilter)       |  X
 *   listFiles(FileFilter)           |  X
 *   mkdir()                         |  mkcolMethod()
 *   mkdirs()                        |  mkcolMethod()
 *   renameTo(File)                  |  moveMethod(String)
 *   setLastModified()               |  X
 *   setReadOnly()                   |  setOverwrite(boolean)
 *   listRoots()                     |  *see WebdavSession
 *   generateFile()                  |
 *   createTempFile(...)             |  setGetTempDir(String)
 *   compareTo(Object)               |  compareTo(Object)
 *   equals(Object)                  |  equals(Object)
 *   hashCode()                      |  X
 * ----------------------------------+-------------------------------------
 *   URL class (JDK 1.3.x)           |  Webdavresource and HttpURL classes
 * ----------------------------------+-------------------------------------
 *   getQuery()                      |  getQuery()
 *   getPath()                       |  getPath()
 *   getUserInfo()                   |  getUserInfo()
 *   getAuthority()                  |  getAuthority()
 *   getPort()                       |  getPort()
 *   getProtocol()                   |  getScheme()
 *   getHost()                       |  getHost()
 *   getFile()                       |  getPath()
 *   getRef()                        |  getFragmenet()
 *   hashCode()                      |  X
 *   sameFile()                      |
 *   toExternalForm()                |  toExternalForm()
 *   openConnection()                |
 *   openStream()                    |
 *   getContent()                    |  getMethodDataAsString()
 * ----------------------------------+-------------------------------------
 *   URLConnection class (JDK 1.3.x) |  HttpClient Library and more
 * ----------------------------------+-------------------------------------
 *   getFileNameMap()                |  X
 *   setFileNameMap()                |  X
 *   connect()                       |
 *   getURL()                        |  HttpURL#getURL()
 *   getContenetLength()()           |
 *   getContentType()                |
 *   getContentEncoding()            |
 *   getExpiration()                 |
 *   getDate()                       |
 *   getLastModified()               |
 *   getHeaderField()                |
 *   getHeaderFieldInt()             |  X
 *   getHeaderFielDate()             |  X
 *   getHeaderFieldKey()             |  X
 *   getHeaderFiled(int)             |  X
 *   getContenet()                   |
 *   getInputStream()                |  WebdavResource#getMethodData()
 *   getOutputStream()               |  WebdavResource#putMethod(...)
 *   setDoInput()                    |  X
 *   getDoInput()                    |  X
 *   setAllowUserInteraction()       |  *see WebdavException and WebdavStatus
 *   getAllowUserInteraction()       |  *see WebdavException and WebdavStatus
 *   setUseCaches()                  |
 *   getUseCaches()                  |
 *   getIfModifiedSince()            |  X
 *   setIfModifiedSince(boolean)     |  X
 *   setRequestProperty(...)         |  X
 *   getRequestProperty(...)         |  X
 *   guessContentTypeFromStream(...) |  X
 * ----------------------------------+-------------------------------------
 * </pre>
 *
 */
public class WebdavResource extends WebdavSession {

    // -------------------------------------------------------  Constructors

    /**
     * The default constructor.
     */
    protected WebdavResource() {
    }

    /**
     * The constructor.
     */
    protected WebdavResource(HttpClient client) {
        super();
        this.client = client;
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param credentials The credentials to use for authentication.
     * @param action The action to set properties of this resource.
     * @param depth The depth to find properties.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, Credentials credentials, int action, int depth)
            throws HttpException, IOException {

        setCredentials(credentials);
        setHttpURL(httpURL, action, depth);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param action The action to set properties of this resource.
     * @param depth The depth to find properties.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, int action, int depth) throws HttpException, IOException {

        setHttpURL(httpURL, action, depth);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param action The action to set properties of this resource.
     * @param depth The depth to find properties.
     * @param followRedirects shall redirects from the server be accepted
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, int action, int depth, boolean followRedirects)
            throws HttpException, IOException {

        setFollowRedirects(this.followRedirects);
        setHttpURL(httpURL, action, depth);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param depth The depth to find properties.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, int depth) throws HttpException, IOException {

        setHttpURL(httpURL, defaultAction, depth);

    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param depth The depth to find properties.
     * @param followRedirects Shall redirects be followed automatically.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, int depth, boolean followRedirects) throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setHttpURL(httpURL, defaultAction, depth);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL) throws HttpException, IOException {

        setHttpURL(httpURL);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param followRedirects shall redirects from the server be accepted
     */
    public WebdavResource(HttpURL httpURL, boolean followRedirects) throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setHttpURL(httpURL);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param proxyHost The hostname of the proxy to use.
     * @param proxyPort The port number of the proxy to use.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, String proxyHost, int proxyPort) throws HttpException, IOException {

        setProxy(proxyHost, proxyPort);
        setHttpURL(httpURL);
    }

    public WebdavResource(HttpURL httpURL, String proxyHost, int proxyPort, boolean followRedirects)
            throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setProxy(proxyHost, proxyPort);
        setHttpURL(httpURL);
    }

    /**
     * The constructor.
     *
     * @param httpURL The specified http URL.
     * @param proxyHost The hostname of the proxy to use.
     * @param proxyPort The port number of the proxy to use.
     * @param proxyCredentials Credentials to use for proxy authentication.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, String proxyHost, int proxyPort, Credentials proxyCredentials)
            throws HttpException, IOException {

        setProxy(proxyHost, proxyPort);
        setProxyCredentials(proxyCredentials);
        setHttpURL(httpURL);
    }

    public WebdavResource(HttpURL httpURL, String proxyHost, int proxyPort, Credentials proxyCredentials,
            boolean followRedirects) throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setProxy(proxyHost, proxyPort);
        setProxyCredentials(proxyCredentials);
        setHttpURL(httpURL);
    }

    /**
     * The constructor.
     * It must be put an escaped http URL as an argument.
     *
     * @param escapedHttpURL The escaped http URL string.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(String escapedHttpURL) throws HttpException, IOException {

        setHttpURL(escapedHttpURL);
    }

    public WebdavResource(String escapedHttpURL, boolean followRedirects) throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setHttpURL(escapedHttpURL);
    }

    /**
     * The constructor.
     * It must be put an escaped http URL as an argument.
     *
     * @param escapedHttpURL The escaped http URL string.
     * @param credentials The credentials used for Authentication.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(String escapedHttpURL, Credentials credentials) throws HttpException, IOException {

        setCredentials(credentials);
        setHttpURL(escapedHttpURL);
    }

    public WebdavResource(String escapedHttpURL, Credentials credentials, boolean followRedirects)
            throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setCredentials(credentials);
        setHttpURL(escapedHttpURL);
    }

    /**
     * The constructor.
     * It must be put an escaped http URL as an argument.
     *
     * @param escapedHttpURL The escaped http URL string.
     * @param proxyHost The hostname of the proxy to use.
     * @param proxyPort The port number of the proxy to use.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(String escapedHttpURL, String proxyHost, int proxyPort)
            throws HttpException, IOException {

        setProxy(proxyHost, proxyPort);
        setHttpURL(escapedHttpURL);
    }

    /**
     * The constructor.
     * It must be put an escaped http URL as an argument.
     *
     * @param escapedHttpURL The escaped http URL string.
     * @param proxyHost The hostname of the proxy to use.
     * @param proxyPort The port number of the proxy to use.
     * @param proxyCredentials Credentials to use for proxy authentication.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(String escapedHttpURL, String proxyHost, int proxyPort, Credentials proxyCredentials)
            throws HttpException, IOException {

        setProxy(proxyHost, proxyPort);
        setProxyCredentials(proxyCredentials);
        setHttpURL(escapedHttpURL);
    }

    /**
     * The constructor.
     *
     * @param httpURL The http URL.
     * @param additionalPath The added relative path.
     * @exception HttpException
     * @exception IOException
     * @see #setDefaultAction(int)
     */
    public WebdavResource(HttpURL httpURL, String additionalPath) throws HttpException, IOException {

        setHttpURL(httpURL, additionalPath);
    }

    /**
     * The constructor.
     *
     * @param httpURL The http URL.
     * @param additionalPath The added relative path.
     * @param followRedirects shall redirects be accepted
     */
    public WebdavResource(HttpURL httpURL, String additionalPath, boolean followRedirects)
            throws HttpException, IOException {

        setFollowRedirects(followRedirects);
        setHttpURL(httpURL, additionalPath);
    }

    // -------------------------------------- Constants for WebDAV properties.

    /**
     * The displayname property.
     */
    public static final String DISPLAYNAME = "displayname";

    /**
     * The getcontentlanguage property.
     */
    public static final String GETCONTENTLANGUAGE = "getcontentlanguage";

    /**
     * The getcontentlength property.
     */
    public static final String GETCONTENTLENGTH = "getcontentlength";

    /**
     * The getlastmodifed property.
     */
    public static final String GETLASTMODIFIED = "getlastmodified";

    /**
     * The creationdate property.
     */
    public static final String CREATIONDATE = "creationdate";

    /**
     * The resourcetype property.
     */
    public static final String RESOURCETYPE = "resourcetype";

    /**
     * The source property.
     */
    public static final String SOURCE = "source";

    /**
     * The getcontenttype property.
     */
    public static final String GETCONTENTTYPE = "getcontenttype";

    /**
     * The getetag property.
     */
    public static final String GETETAG = "getetag";

    /**
     * The ishidden property.
     */
    public static final String ISHIDDEN = "ishidden";

    /**
     * The iscollection property.
     */
    public static final String ISCOLLECTION = "iscollection";

    /**
     * The supportedlock property.
     */
    public static final String SUPPORTEDLOCK = "supportedlock";

    /**
     * The lockdiscovery property.
     */
    public static final String LOCKDISCOVERY = "lockdiscovery";

    // ------------------------------------------------------------ Constants

    /**
     * No action to find properties for this resource.
     */
    public static final int NOACTION = 1;

    /**
     * The action setting only the displayname for this resource.
     */
    public static final int NAME = 2;

    /**
     * The action setting the basic properties for this resource.
     */
    public static final int BASIC = 3;

    /**
     * The action setting the default DAV properties for this resource.
     */
    public static final int DEFAULT = 4;

    /**
     * The action setting the all properties for this resource.
     */
    public static final int ALL = 5;

    /**
     *
     */
    public static final int OPTIONS_WORKSPACE = 8;

    /**
     *
     */
    public static final int OPTIONS_VERSION_HISTORY = 9;

    public static final int LABEL_SET = 10;
    public static final int LABEL_REMOVE = 11;
    public static final int LABEL_ADD = 12;

    /**
     * Owner information for locking and unlocking.
     */
    public static final String defaultOwner = "Slide";

    /**
     * The true constant string.
     */
    public static final String TRUE = "1";

    /**
     * The false constant string.
     */
    public static final String FALSE = "0";

    /**
     * Date formats using for Date parsing.
     */
    public static final SimpleDateFormat formats[] = {
            new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
            new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
            new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US),
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US) };

    /**
     * GMT timezone.
     */
    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");

    static {
        for (int i = 0; i < formats.length; i++) {
            formats[i].setTimeZone(gmtZone);
        }
    }

    // --------------------------------------------------- Instance Variables

    /**
     * The HttpURL to represent a WebDAV resource.
     */
    protected HttpURL httpURL;

    /**
     * Table of the hrefs gotten in a collection.
     */
    protected WebdavResources childResources = new WebdavResources();

    /**
     * The default action to find properties.
     */
    protected static int defaultAction = BASIC;

    /**
     * The default depth for WebDAV methods.
     */
    protected static int defaultDepth = DepthSupport.DEPTH_0;

    /**
     * The default temporary directory for the GET method.
     * @deprecated The client is responsible for disk I/O.
     */
    protected static String tempDirForGet;

    /**
     * The flag setter to use the disk for the GET method.
     * @deprecated The client is responsible for disk I/O.
     */
    protected static boolean useDiskForGet = true;

    /**
     * The flag to set the status code by propfind.
     */
    protected boolean thisResource;

    /**
     * The allowed HTTP methods.
     */
    protected Enumeration allowedMethods;

    /**
     * The capabilities of the WebDAV server.
     */
    protected Enumeration davCapabilities;

    /**
     * An WebdavResource flag to check its existence;
     */
    protected boolean exists;

    /**
     * An WebdavResource flag to check overwriting;
     */
    protected boolean overwrite;

    /**
     * An status code performed by HTTP methods at the most recent.
     */
    protected int latestStatusCode;

    /**
     * An status message performed by HTTP methods at the most recent.
     */
    protected String latestStatusMessage = "";

    /**
     * An WebDAV property, displayname.
     */
    protected String displayName = "";

    /**
     * An WebDAV property, getcontentlength.
     */
    protected long getContentLength;

    /**
     * An WebDAV property, getcontenttype.
     */
    protected String getContentType = "";

    /**
     * An WebDAV property, resourcetype.
     */
    protected ResourceTypeProperty resourceType;

    /**
     * An WebDAV property, getlastmodified.
     */
    protected long getLastModified;

    /**
     * An WebDAV property, creationdate.
     */
    protected long creationDate;

    /**
     * An WebDAV property, getetag.
     */
    protected String getEtag = "";

    /**
     * Owner information for locking and unlocking.
     */
    protected String owner = null;

    /**
     * An WebDAV property, ishidden.
     */
    protected boolean isHidden;

    /**
     * An WebDAV property, iscollection.
     */
    protected boolean isCollection;

    /**
     * An WebDAV property, supportedlock.
     */
    protected String supportedLock = "";

    /**
     * An WebDAV property, lockdiscovery.
     */
    protected LockDiscoveryProperty lockDiscovery;

    protected boolean followRedirects = false;

    /**
     * Map of additional headers
     */
    protected Map headers = new HashMap();

    // --------------------------------------------------------- Basic settings

    /**
     * Generates and adds the "Transaction" header if this method is part of
     * an externally controlled transaction.
     */
    protected void generateTransactionHeader(HttpMethod method) {
        if (client == null || method == null)
            return;

        WebdavState state = (WebdavState) client.getState();
        String txHandle = state.getTransactionHandle();
        if (txHandle != null) {
            method.setRequestHeader("Transaction", "<" + txHandle + ">");
        }
    }

    /**
     * Generate and add the If header to the specified HTTP method.
     */
    protected void generateIfHeader(HttpMethod method) {

        if (client == null)
            return;
        if (method == null)
            return;

        WebdavState state = (WebdavState) client.getState();
        String[] lockTokens = state.getAllLocks(method.getPath());

        if (lockTokens.length == 0)
            return;

        StringBuffer ifHeaderValue = new StringBuffer();

        for (int i = 0; i < lockTokens.length; i++) {
            ifHeaderValue.append("(<").append(lockTokens[i]).append(">) ");
        }

        method.setRequestHeader("If", ifHeaderValue.toString());

    }

    /**
     * Add all additionals headers that have been previously registered
     * with addRequestHeader to the method
     */
    protected void generateAdditionalHeaders(HttpMethod method) {
        for (Iterator iterator = headers.keySet().iterator(); iterator.hasNext();) {
            String header = (String) iterator.next();
            method.setRequestHeader(header, (String) headers.get(header));
        }
    }

    /**
     * Parse the <code>java.util.Date</code> string for HTTP-date.
     *
     * @return The parsed date.
     */
    protected Date parseDate(String dateValue) {
        // TODO: move to the common util package related to http.
        Date date = null;
        for (int i = 0; (date == null) && (i < formats.length); i++) {
            try {
                synchronized (formats[i]) {
                    date = formats[i].parse(dateValue);
                }
            } catch (ParseException e) {
            }
        }

        return date;
    }

    /**
     * Set only the displayname property for this resource.
     *
     * @param depth The depth to find properties.
     */
    protected void setNameProperties(int depth) throws HttpException, IOException {

        Vector properties = new Vector();
        properties.addElement(DISPLAYNAME);

        setNamedProp(depth, properties);
    }

    /**
     * Sets the basic properties on a resource by indirectly issuing a PROPFIND
     * on the resource.
     *
     * <p>Properties retrieved include:
     *
     * <ul>
     *  <li>displayname</li>
     *  <li>getcontentlength</li>
     *  <li>getcontenttype</li>
     *  <li>resourcetype</li>
     *  <li>getlastmodified</li>
     *  <li>lockdiscovery</li>
     * </ul>
     *
     * @param depth The depth to find properties.
     */
    protected void setBasicProperties(int depth) throws HttpException, IOException {

        Vector properties = new Vector();
        properties.addElement(DISPLAYNAME);
        properties.addElement(GETCONTENTLENGTH);
        properties.addElement(GETCONTENTTYPE);
        properties.addElement(RESOURCETYPE);
        properties.addElement(GETLASTMODIFIED);
        properties.addElement(LOCKDISCOVERY);

        setNamedProp(depth, properties);
    }

    /**
     * Set the default properties on the resource by indirectly issuing a PROPFIND request
     * for a default set of properties.
     *
     * <p>Properties retrieved include:
     *
     * <ul>
     *  <li>creationdate</li>
     *  <li>displayname</li>
     *  <li>getcontentlanguage</li>
     *  <li>getcontentlength</li>
     *  <li>getcontenttype</li>
     *  <li>getetag</li>
     *  <li>getlastmodified</li>
     *  <li>lockdiscovery</li>
     *  <li>resourcetype</li>
     *  <li>source</li>
     *  <li>supportedlock</li>
     * </ul>
     *
     * @param depth The depth to find properties.
     */
    protected void setDefaultProperties(int depth) throws HttpException, IOException {

        Vector properties = new Vector();
        properties.addElement(CREATIONDATE);
        properties.addElement(DISPLAYNAME);
        properties.addElement(GETCONTENTLANGUAGE);
        properties.addElement(GETCONTENTLENGTH);
        properties.addElement(GETCONTENTTYPE);
        properties.addElement(GETETAG);
        properties.addElement(GETLASTMODIFIED);
        properties.addElement(LOCKDISCOVERY);
        properties.addElement(RESOURCETYPE);
        properties.addElement(SOURCE);
        properties.addElement(SUPPORTEDLOCK);

        setNamedProp(depth, properties);
    }

    /**
     * Set the named properties for this resource.
     *
     * @param depth The depth.
     * @param propertyNames The property-names.
     */
    protected void setNamedProp(int depth, Vector propertyNames) throws HttpException, IOException {

        Enumeration responses = propfindMethod(depth, propertyNames);
        setWebdavProperties(responses);
    }

    /**
     * Set all properties for this resource.
     *
     * @param depth The depth
     */
    protected void setAllProp(int depth) throws HttpException, IOException {

        Enumeration responses = propfindMethod(depth);
        setWebdavProperties(responses);
    }

    /**
     * Set WebDAV properties following to the given http URL.
     * This method is fundamental for getting information of a collection.
     *
     * @param responses An enumeration over {@link ResponseEntity} items, one
     * for each resource for which information was returned via PROPFIND.
     *
     * @exception HttpException
     * @exception IOException The socket error with a server.
     */
    protected void setWebdavProperties(Enumeration responses) throws HttpException, IOException {

        // Make the resources in the collection empty.
        childResources.removeAll();
        while (responses.hasMoreElements()) {

            ResponseEntity response = (ResponseEntity) responses.nextElement();

            boolean itself = false;
            String href = response.getHref();
            if (!href.startsWith("/"))
                href = URIUtil.getPath(href);
            href = decodeMarks(href);

            /*
             * Decode URIs to common (unescaped) format for comparison 
             * as HttpClient.URI.setPath() doesn't escape $ and : chars.
             */
            String httpURLPath = httpURL.getPath();
            String escapedHref = URIUtil.decode(href);

            // Normalize them to both have trailing slashes if they differ by one in length.
            int lenDiff = escapedHref.length() - httpURLPath.length();
            int compareLen = 0;

            if (lenDiff == -1 && !escapedHref.endsWith("/")) {
                compareLen = escapedHref.length();
                lenDiff = 0;
            } else if (lenDiff == 1 && !httpURLPath.endsWith("/")) {
                compareLen = httpURLPath.length();
                lenDiff = 0;
            }

            // if they are the same length then compare them.
            if (lenDiff == 0) {
                if ((compareLen == 0 && httpURLPath.equals(escapedHref))
                        || httpURLPath.regionMatches(0, escapedHref, 0, compareLen)) {
                    // escaped href and http path are the same
                    // Set the status code for this resource.
                    if (response.getStatusCode() > 0)
                        setStatusCode(response.getStatusCode());
                    setExistence(true);
                    itself = true;
                }
            }

            // Get to know each resource.
            WebdavResource workingResource = null;
            if (itself) {
                workingResource = this;
            } else {
                workingResource = createWebdavResource(client);
                workingResource.setDebug(debug);
            }

            // clear the current lock set
            workingResource.setLockDiscovery(null);

            // Process the resource's properties
            Enumeration properties = response.getProperties();
            while (properties.hasMoreElements()) {

                Property property = (Property) properties.nextElement();

                // ------------------------------  Checking WebDAV properties
                workingResource.processProperty(property);
            }

            String displayName = workingResource.getDisplayName();

            if (displayName == null || displayName.trim().equals("")) {
                displayName = getName(href, true);
            }
            if (!itself) {
                String myURI = httpURL.getEscapedURI();
                char[] childURI = (myURI + (myURI.endsWith("/") ? "" : "/") + getName(href, false)).toCharArray();
                HttpURL childURL = httpURL instanceof HttpsURL ? new HttpsURL(childURI) : new HttpURL(childURI);
                childURL.setRawAuthority(httpURL.getRawAuthority());
                workingResource.setHttpURL(childURL, NOACTION, defaultDepth);
                workingResource.setExistence(true);
                workingResource.setOverwrite(getOverwrite());
            }
            workingResource.setDisplayName(displayName);

            if (!itself)
                childResources.addResource(workingResource);
        }
    }

    // ------------------------------------------------------------ Properties

    /**
     * Set the default action for this resource.
     * The default action is set as 'BASIC' for the first time.
     *
     * ex)
     *  WebdavResource.NOACTION
     *  WebdavResource.NAME
     *  WebdavResource.BASIC
     *  WebdavResource.DEFAULT
     *  WebdavResource.ALL
     *
     * @param action The action type.
     * @see #NOACTION
     * @see #NAME
     * @see #BASIC
     * @see #DEFAULT
     * @see #ALL
     */
    public static void setDefaultAction(int action) {
        defaultAction = action;
    }

    /**
     * Get the default action.
     *
     * @return The action type.
     */
    public static int getDefaultAction() {
        return defaultAction;
    }

    /**
     * Set the default action for this resource.
     *
     * ex)
     *   DepthSupport.DEPTH_0
     *   DepthSupport.DEPTH_1
     *   DepthSupport.DEPTH_INFINITY
     *
     * @param depth The depth.
     */
    public static void setDefaultDepth(int depth) {
        defaultDepth = depth;
    }

    /**
     * Get the default action.
     *
     * @return The depth.
     */
    public static int getDefaultDepth() {
        return defaultDepth;
    }

    /**
     * Get the default temporary directory for the GET method.
     *
     * @param tempDir The temporary directory.
     * @deprecated The given directory will not be used.
     */
    public static void setGetTempDir(String tempDir) {
        tempDirForGet = tempDir;
    }

    /**
     * Get the default temporary directory for the GET method.
     * The default temporary directory is "temp/".
     *
     * @return The temporary directory path.
     *         It's set by default, if it returns null.
     * @deprecated The returned directory is not used by the GET method.
     */
    public static String getGetTempDir() {
        return tempDirForGet;
    }

    /**
     * Set the use disk flag for the GET method.
     *
     * @param useDisk The use disk flag.
     * @deprecated This method has no effect.
     */
    public static void setGetUseDisk(boolean useDisk) {
        //useDiskForGet = useDisk;
    }

    /**
     * Get the use disk flag for the GET method.
     *
     * @return The current flag of the use disk.
     *         By default, it's true.
     * @deprecated This method always returns false.
     */
    public static boolean getGetUseDisk() {
        return false;
    }

    /**
     * Sets a flag indicating that redirect responses from
     * the server shall be followed.
     */
    public void setFollowRedirects(boolean value) {
        this.followRedirects = value;
    }

    /**
     * Returns the current "follow redirects" flag.
     * @see #setFollowRedirects(boolean)
     */
    public boolean getFollowRedirects() {
        return this.followRedirects;
    }

    /**
     * Test that the httpURL is the same with the client.
     *
     * @return true if the given httpURL is the client for this resource.
     */
    protected synchronized boolean isTheClient() throws URIException {
        HostConfiguration hostConfig = client.getHostConfiguration();
        Credentials creds = client.getState().getCredentials(null, hostConfig.getHost());
        String userName = null;
        String password = null;

        if (creds instanceof UsernamePasswordCredentials) {
            UsernamePasswordCredentials upc = (UsernamePasswordCredentials) creds;
            userName = upc.getUserName();
            password = upc.getPassword();
        }
        String ref = httpURL.getUser();
        boolean userMatches = userName != null ? userName.equals(ref) : ref == null;
        if (userMatches) {
            ref = httpURL.getPassword();
            userMatches = password != null ? password.equals(ref) : ref == null;
        } else {
            return false;
        }
        if (userMatches) {
            return httpURL.getHost().equalsIgnoreCase(hostConfig.getHost())
                    && httpURL.getPort() == hostConfig.getProtocol().resolvePort(hostConfig.getPort());
        }
        return false;
    }

    /**
     * Set the client for this resource.
     *
     * @exception IOException
     */
    protected void setClient() throws IOException {
        setClient(httpURL);
    }

    /**
     * Set the client for this resource and the given http URL.
     *
     * @param httpURL The http URL.
     * @exception IOException
     */
    protected synchronized void setClient(HttpURL httpURL) throws IOException {

        if (client == null) {
            client = getSessionInstance(httpURL);
        } else if (!isTheClient()) {
            closeSession();
            client = getSessionInstance(httpURL);
        }
    }

    /**
     * Set the HttpURL for this WebdavResource.
     *
     * @param httpURL the specified HttpURL.
     * @param action The action to decide, which properties to find.
     * @param depth The depth to find properties.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     * @see #setDefaultAction(int)
     */
    public void setHttpURL(HttpURL httpURL, int action, int depth) throws HttpException, IOException {

        this.httpURL = httpURL;
        setClient(httpURL);
        // make its existence false
        setExistence(false);

        try {
            setProperties(action, depth);
        } catch (Exception e) {
            // Ignore the exception if default properties cannot be loaded 
        }
    }

    /**
     * Set the HttpURL for this WebdavResource.
     *
     * @param httpURL the specified HttpURL.
     * @param depth The depth to find properties.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     */
    public void setHttpURL(HttpURL httpURL, int depth) throws HttpException, IOException {

        // Follow the default action.
        setHttpURL(httpURL, defaultAction, depth);
    }

    /**
     * Set the HttpURL for this WebdavResource.
     * It must be put an escaped path part of the http URL as an argument.
     *
     * @param httpURL The specified HttpURL.
     * @param additionalPath The added relative path.
     * @param action The action to decide, which properties to find.
     * @param depth The depth.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     * @see #setDefaultAction(int)
     */
    public void setHttpURL(HttpURL httpURL, String additionalPath, int action, int depth)
            throws HttpException, IOException {

        setHttpURL(httpURL instanceof HttpsURL ? new HttpsURL((HttpsURL) httpURL, additionalPath)
                : new HttpURL(httpURL, additionalPath), action, depth);
    }

    /**
     * Set the HttpURL for this WebdavResource.
     * It must be put an escaped path part of the http URL as an argument.
     *
     * @param httpURL The specified HttpURL.
     * @param additionalPath The added relative path.
     * @param action The action to decide, which properties to find.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     * @see #setDefaultAction(int)
     */
    public void setHttpURL(HttpURL httpURL, String additionalPath, int action) throws HttpException, IOException {

        setHttpURL(httpURL instanceof HttpsURL ? new HttpsURL((HttpsURL) httpURL, additionalPath)
                : new HttpURL(httpURL, additionalPath), action, defaultDepth);
    }

    /**
     * Set the HttpURL for this WebdavResource.
     *
     * @param httpURL The specified HttpURL.
     * @param additionalPath The added relative path.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     */
    public void setHttpURL(HttpURL httpURL, String additionalPath) throws HttpException, IOException {

        setHttpURL(httpURL instanceof HttpsURL ? new HttpsURL((HttpsURL) httpURL, additionalPath)
                : new HttpURL(httpURL, additionalPath), defaultAction, defaultDepth);
    }

    /**
     * Set the HttpURL for this WebdavResource.
     *
     * @param httpURL the specified HttpURL.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     */
    public void setHttpURL(HttpURL httpURL) throws HttpException, IOException {

        setHttpURL(httpURL, defaultDepth);
    }

    /**
     * Set the HttpURL of this WebdavResource.
     * It must be put an escaped http URL as an argument.
     *
     * @param escapedHttpURL The escaped http URL string.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(HttpURL)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     * @see #setPath(java.lang.String)
     */
    public void setHttpURL(String escapedHttpURL) throws HttpException, IOException {

        setHttpURL(escapedHttpURL.startsWith("https") ? new HttpsURL(escapedHttpURL) : new HttpURL(escapedHttpURL));
    }

    /**
     * Get the HttpURL of this WebdavResource.
     *
     * @return httpURL the http URL.
     */
    public HttpURL getHttpURL() {
        return httpURL;
    }

    /**
     * Get the HttpURL except for userinfo.
     *
     * @return httpURL the http URL.
     */
    public HttpURL getHttpURLExceptForUserInfo() throws URIException {

        return httpURL instanceof HttpsURL ? new HttpsURL(httpURL.getRawURI()) : new HttpURL(httpURL.getRawURI());
    }

    /**
     * Set the path part of this WebdavResource.
     *
     * @param path the specified path.
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(HttpURL)
     * @see #setHttpURL(java.lang.String)
     * @see #setUserInfo(java.lang.String, java.lang.String)
     */
    public void setPath(String path) throws HttpException, IOException {

        httpURL.setPath(path);
        setHttpURL(httpURL);
    }

    /**
     * Get the path part of this WebdavResource.
     * If the decoding of the path fails, this method will not throw an
     * exception but return the escaped path instead.
     *
     * @return the path for this WebdavResource.
     * @see org.apache.commons.httpclient.HttpURL#getPath()
     * @see #setPath(java.lang.String)
     */
    public String getPath() {
        try {
            return httpURL.getPath();
        } catch (URIException e) {
            return httpURL.getEscapedPath();
        }
    }

    /**
     * Get the name of this WebdavResource.
     * If the decoding of the name fails, this method will not throw an
     * exception but return the escaped name instead.
     *
     * @return the name of this WebdavResource.
     * @see org.apache.commons.httpclient.HttpURL#getName()
     */
    public String getName() {
        return getName(httpURL.getEscapedPath(), true);
    }

    /**
     * Get the hostname of this WebdavResource.
     *
     * @return the hostname.
     * @exception URIException
     */
    public String getHost() throws URIException {
        return httpURL.getHost();
    }

    /**
     * Set the userinfo part of this WebdavResource.
     *
     * @exception HttpException
     * @exception IOException
     * @see #setHttpURL(HttpURL)
     * @see #setHttpURL(java.lang.String)
     * @see #setPath(java.lang.String)
     */
    public void setUserInfo(String userName, String password) throws HttpException, IOException {

        httpURL.setUserinfo(userName, password);
        setHttpURL(httpURL);
    }

    /**
     * Add a header in the request sent to the webdav server
     *
     * @param header Header name
     * @param value Value
     */
    public void addRequestHeader(String header, String value) {
        headers.put(header, value);
    }

    // ------------------------------------------------ DAV properties checking

    /**
     * Get the value of DAV property, displayname.
     *
     * @return The displayname string.
     */
    public String getDisplayName() {
        return displayName;
    }

    /**
     * Set the value of DAV property, displayname.
     *
     * @param displayName The displayname string.
     */
    protected void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    /**
     * Get the value of DAV property, getcontentlength.
     *
     * @return The getcontentlength value.
     */
    public long getGetContentLength() {
        return getContentLength;
    }

    /**
     * Set the value of DAV property, getcontentlength.
     *
     * @param getContentLength The getcontentlength value.
     */
    protected void setGetContentLength(long getContentLength) {
        this.getContentLength = getContentLength;
    }

    /**
     * Set the value of DAV property, getcontentlength.
     *
     * @param getContentLength The getcontentlength value.
     */
    protected void setGetContentLength(String getContentLength) {
        try {
            this.getContentLength = Long.parseLong(getContentLength);
        } catch (NumberFormatException nfe) {
            // it's ok to ignore this error.
        }
    }

    /**
     * Get the value of DAV property, resourcetype.
     *
     * @return The resourcetype property.
     * @see #isCollection()
     */
    public ResourceTypeProperty getResourceType() {
        return resourceType;
    }

    /**
     * Set the value of DAV property, resourcetype.
     *
     * @param resourceType The resourcetype property.
     */
    protected void setResourceType(ResourceTypeProperty resourceType) {
        this.resourceType = resourceType;
    }

    /**
     * Get the value of DAV property, resourcetype
     *
     * @return The resourcetype string.
     * @see #getResourceType()
     * @see #getIsCollection()
     */
    public boolean isCollection() {
        if (getResourceType() == null)
            return false;
        return getResourceType().isCollection();
    }

    /**
     * Get the value of DAV property, getcontenttype.
     *
     * @return The getcontenttype string.
     */
    public String getGetContentType() {
        return getContentType;
    }

    /**
     * Set the value of DAV property, getcontenttype.
     *
     * @param getContentType The getcontenttype string.
     */
    protected void setGetContentType(String getContentType) {
        this.getContentType = getContentType;
    }

    /**
     * Set the content-type to use for this resource, for PUTs.
     * @param contentType The content-type string.
     */
    public void setContentType(String contentType) {
        this.getContentType = contentType;
    }

    /**
     * Get the value of DAV property, getlastmodified.
     *
     * @return The getlastmodified value.
     */
    public long getGetLastModified() {
        return getLastModified;
    }

    /**
     * Set the value of DAV property, getlastmodified.
     *
     * @param getLastModified The getlastmodified value.
     * @see #setGetLastModified(java.lang.String)
     */
    protected void setGetLastModified(long getLastModified) {
        this.getLastModified = getLastModified;
    }

    /**
     * Set the value of DAV property, getlastmodified.
     *
     * @param getLastModified The getlastmodified value.
     * @see #setGetLastModified(long)
     */
    protected void setGetLastModified(String getLastModified) {
        Date date = parseDate(getLastModified);
        if (date != null)
            this.getLastModified = date.getTime();
    }

    /**
     * Get the value of DAV property, creationdate.
     *
     * @return The creationdate string.
     */
    public long getCreationDate() {
        return creationDate;
    }

    /**
     * Set the value of DAV property, creationdate.
     *
     * @param creationDate The creationdate string.
     */
    protected void setCreationDate(long creationDate) {
        this.creationDate = creationDate;
    }

    /**
     * Set the value of DAV property, creationdate.
     *
     * @param creationDate The creationdate string.
     */
    protected void setCreationDate(String creationDate) {
        Date date = parseDate(creationDate);
        if (date != null)
            this.creationDate = date.getTime();
    }

    /**
     * Get the value of DAV property, getetag.
     *
     * @return The getetag string.
     */
    public String getGetEtag() {
        return getEtag;
    }

    /**
     * Set the value of DAV property, getetag.
     *
     * @param getEtag The getetag string.
     */
    protected void setGetEtag(String getEtag) {
        this.getEtag = getEtag;
    }

    /**
     * Get the owner string, as used for locking purposes.
     */
    public String getOwner() {
        return owner;
    }

    /**
     * Get the value of DAV property, supportedlock.
     *
     * @return The supportedlock string.
     */
    public String getSupportedLock() {
        return supportedLock;
    }

    /**
     * Set the value of DAV property, supportedlock.
     *
     * @param supportedLock The supportedlock string.
     */
    protected void setSupportedLock(String supportedLock) {
        this.supportedLock = supportedLock;
    }

    /**
     * Get the value of DAV property, lockdiscovery.
     *
     * @return The lockdiscovery property.
     */
    public LockDiscoveryProperty getLockDiscovery() {
        return lockDiscovery;
    }

    /**
     * Set the value of DAV property, lockdiscovery.
     *
     * @param lockDiscovery The lockdiscovery property.
     */
    protected void setLockDiscovery(LockDiscoveryProperty lockDiscovery) {
        this.lockDiscovery = lockDiscovery;
    }

    /**
     * Get the activelock owners for this resource.
     *
     * @return An enumeration of owners.
     */
    public Enumeration getActiveLockOwners() {
        if (lockDiscovery == null)
            return null;
        Lock[] activeLocks = lockDiscovery.getActiveLocks();
        if (activeLocks == null)
            return null;
        Vector buff = new Vector();
        int count = activeLocks.length;
        for (int i = 0; i < count; i++) {
            buff.addElement(activeLocks[i].getOwner());
        }
        return buff.elements();
    }

    /**
     * Test that this resource is locked.
     *
     * @return true if it's locked.
     */
    public boolean isLocked() {
        if (lockDiscovery == null)
            return false;
        Lock[] activeLocks = lockDiscovery.getActiveLocks();
        if (activeLocks == null)
            return false;
        for (int i = 0; i < activeLocks.length; i++) {
            if (activeLocks[i].getLockType() == Lock.TYPE_WRITE)
                return true;
        }
        return false;
    }

    /**
     * Get the value of DAV property, ishidden.
     *
     * @return true if it is hidden, otherwise false.
     */
    public boolean getIsHidden() {
        return isHidden;
    }

    /**
     * Set the value of DAV property, ishidden.
     *
     * @param isHidden
     */
    protected void setIsHidden(boolean isHidden) {
        this.isHidden = isHidden;
    }

    /**
     * Set the value of DAV property, ishidden.
     *
     * @param isHidden
     */
    protected void setIsHidden(String isHidden) {
        this.isHidden = isHidden.equals(TRUE) ? true : false;
    }

    /**
     * Get the value of DAV property, iscollection
     *
     * @return true if it is collection, otherwise false.
     * @see #isCollection()
     */
    public boolean getIsCollection() {
        return isCollection;
    }

    /**
     * Set the value of DAV property, iscollection
     *
     * @param isCollection
     */
    protected void setIsCollection(boolean isCollection) {
        this.isCollection = isCollection;
    }

    /**
     * Set the value of DAV property, iscollection
     *
     * @param isCollection
     */
    protected void setIsCollection(String isCollection) {
        this.isCollection = isCollection.equals(TRUE) ? true : false;
    }

    // --------------------------------------- WebDAV Resource Public Methods

    /**
     * Set the properties for this resource.
     *
     * @param action The action to find properties for this resource.
     * @param depth the depth to which properties shall be found
     * @see #setDefaultAction(int)
     */
    public void setProperties(int action, int depth) throws HttpException, IOException {

        switch (action) {
        case NAME:
            setNameProperties(depth);
            break;
        case BASIC:
            setBasicProperties(depth);
            break;
        case DEFAULT:
            setDefaultProperties(depth);
            break;
        case ALL:
            setAllProp(depth);
            break;
        case NOACTION:
        default:
            break;
        }
    }

    /**
     * Set the properties for this resource.
     *
     * @param depth the depth to which properties shall be found
     */
    public void setProperties(int depth) throws HttpException, IOException {

        setProperties(defaultAction, depth);
    }

    /**
     * Refresh the properties of this resource
     * without changing the status of the previous command
     */
    protected void refresh() throws HttpException, IOException {
        int latestStatusCode = this.latestStatusCode;
        String latestStatusMessage = this.latestStatusMessage;
        setProperties(DepthSupport.DEPTH_0);
        this.latestStatusCode = latestStatusCode;
        this.latestStatusMessage = latestStatusMessage;
    }

    /**
     * Returns the last known information about the existence of this resource.
     * This is a wrapper method for getExistence.
     *
     * A previous call to the method setProperties might be necessary to update
     * that information.
     *  
     * @return true if the resource is known to exist<br>
     *         false if the resource is known not to exist or its status is unknown.
     * @see #getExistence()
     * @see #setProperties(int, int)
     */
    public boolean exists() {
        return getExistence();
    }

    /**
     * Set its existence.
     *
     * @param exists The boolean value to be set for its existence.
     */
    protected void setExistence(boolean exists) {
        this.exists = exists;
    }

    /**
     * Returns the last known information about the existence of this resource.
     *
     * A previous call to the method setProperties might be necessary to update that
     * information.
     *  
     * @return true if the resource is known to exist<br>
     *         false if the resource is known not to exist or its status is unknown.
     * @see #setProperties(int, int)
     */
    public boolean getExistence() {
        return exists;
    }

    /**
     * Set the overwrite flag for COPY, MOVE, BIND and REBIND.
     * Should be set before the method is executed.
     *
     * @param overwrite the overwrite flag
     * @see #getOverwrite()
     */
    public void setOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
    }

    /**
     * Get the current value of the overwrite flag for COPY, MOVE, BIND and
     * REBIND.
     *
     * @return true if the current flag is overwriting.
     * @see #setOverwrite(boolean)
     */
    public boolean getOverwrite() {
        return overwrite;
    }

    /**
     * Close the session of this client
     */
    public void close() throws IOException {
        closeSession();
    }

    /**
     * Get the lastest value of the status message by HTTP methods.
     *
     * @return The http status string.
     */
    public String getStatusMessage() {
        return latestStatusMessage;
    }

    /**
     * Get the lastest value of the status code by HTTP methods.
     *
     * @return The http status code.
     */
    public int getStatusCode() {
        return latestStatusCode;
    }

    /**
     * Set the lastest value of the status code by HTTP methods.
     *
     * @param statusCode the HTTP status code.
     */
    protected void setStatusCode(int statusCode) {
        setStatusCode(statusCode, null);
    }

    /**
     * Set the lastest value of the status code by HTTP methods.
     *
     * @param statusCode the HTTP status code.
     * @param message the additional message.
     */
    protected void setStatusCode(int statusCode, String message) {

        latestStatusCode = statusCode;
        latestStatusMessage = WebdavStatus.getStatusText(statusCode) + " (" + statusCode + ")"
                + ((message == null) ? "" : message);
    }

    /**
     * Get the allowed methods, checked by HTTP OPTIONS.
     *
     * @return the allowed HTTP methods.
     * @see #optionsMethod(java.lang.String)
     */
    public Enumeration getAllowedMethods() {
        return allowedMethods;
    }

    /**
     * Get the WebDAV capabilities, checked by HTTP OPTIONS.
     *
     * @return the WebDAV capabilities.
     * @see #optionsMethod(java.lang.String)
     */
    public Enumeration getDavCapabilities() {
        return davCapabilities;
    }

    /**
     * Get all resources in this collection with the depth 1.
     *
     * @return resources in this collection with the depth 1.
     * @exception HttpException
     * @exception IOException
     */
    public WebdavResources getChildResources() throws HttpException, IOException {

        setProperties(DepthSupport.DEPTH_1);

        return childResources;
    }

    /**
     * Get an array of resources denoting the WebDAV child resources in the
     * collection of this resources.
     *
     * @return An array of child resources in this resource.
     * @exception HttpException
     * @exception IOException
     */
    public WebdavResource[] listWebdavResources() throws HttpException, IOException {

        return getChildResources().listResources();
    }

    /**
     * Get an array of pathnames denoting the WebDAV resources in the
     * collection denoted by this pathname.
     *
     * @return An array of pathnames denoting the resources, null if an
     *         IOException occurs.
     */
    public String[] list() {

        try {
            setNameProperties(DepthSupport.DEPTH_1);
        } catch (IOException e) {
            return null;
        }
        Enumeration hrefs = childResources.getResourceNames();

        // To be atomic.
        Vector hrefList = new Vector();
        while (hrefs.hasMoreElements()) {
            hrefList.addElement((String) hrefs.nextElement());
        }
        // Calculate the size of the string array.
        int num = hrefList.size();
        String[] pathnames = new String[num];
        for (int i = 0; i < num; i++) {
            pathnames[i] = (String) hrefList.elementAt(i);
        }

        return pathnames;
    }

    /**
     * Get an array of pathnames and basic information denoting the WebDAV
     * resources in the denoted by this pathname.
     *
     * array 0: displayname
     * array 1: getcontentlength
     * array 2: iscollection or getcontentype
     * array 3: getlastmodifieddate
     * array 4: name
     *
     * @return An array of pathnames and more denoting the resources.
     * @exception HttpException
     * @exception IOException
     */
    public Vector listBasic() throws HttpException, IOException {

        setBasicProperties(DepthSupport.DEPTH_1);
        Enumeration hrefs = childResources.getResourceNames();

        Vector hrefList = new Vector();
        while (hrefs.hasMoreElements()) {
            try {
                String resourceName = (String) hrefs.nextElement();
                WebdavResource currentResource = childResources.getResource(resourceName);

                String[] longFormat = new String[5];
                // displayname.
                longFormat[0] = currentResource.getDisplayName();

                long length = currentResource.getGetContentLength();
                // getcontentlength
                longFormat[1] = new Long(length).toString();
                // resourcetype
                ResourceTypeProperty resourceTypeProperty = currentResource.getResourceType();
                // getcontenttype
                String getContentType = currentResource.getGetContentType();
                longFormat[2] = resourceTypeProperty.isCollection() ? "COLLECTION" : getContentType;
                Date date = new Date(currentResource.getGetLastModified());
                // getlastmodified
                // Save the dummy what if failed.
                longFormat[3] = (date == null) ? "-- -- ----" :
                // Print the local fancy date format.
                        DateFormat.getDateTimeInstance().format(date);
                hrefList.addElement(longFormat);

                // real name of componente
                longFormat[4] = currentResource.getName();

            } catch (Exception e) {
                // FIXME: After if's gotten an exception, any solution?
                if (debug > 0)
                    e.printStackTrace();
                //log.error(e,e);
            }
        }

        return hrefList;
    }

    /**
     * Set the URL encoding flag for this http URL.
     *
     * @param encodeURLs true if it is encoded.
     * @exception MalformedURLException
     * @exception IOException
     *
     * @deprecated  No longer has any effect.
     */
    public void setEncodeURLs(boolean encodeURLs) {

    }

    // -------------------------- General accessor to use http request methods.

    /**
     * Retrieve the current http client for this resource.
     *
     * @return The http client.
     * @see #executeHttpRequestMethod(HttpClient, HttpMethod)
     */
    public HttpClient retrieveSessionInstance() throws IOException {

        setClient();
        return client;
    }

    /**
     * Execute the http request method.  And get its status code.
     *
     * @param client The http client.
     * @param method The http method.
     * @return The status code.
     * @see #retrieveSessionInstance()
     */
    public int executeHttpRequestMethod(HttpClient client, HttpMethod method) throws IOException, HttpException {

        client.executeMethod(method);
        return method.getStatusCode();
    }

    // --------------------------------- WebDAV Request-method Public Methods

    /**
     * Updates the resource with a new set of aces.
     *
     * @param path the server relative path of the resource to which the given
     *        ACEs shall be applied
     * @param aces the ACEs to apply
     * @return true if the method succeeded
     */
    public boolean aclMethod(String path, Ace[] aces) throws HttpException, IOException {

        setClient();

        AclMethod method = new AclMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        for (int i = 0; i < aces.length; i++) {
            Ace ace = aces[i];
            method.addAce(ace);
        }

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Return the <code>AclProperty</code> for the current resource
     *
     * @return acl property, null if the server doesn't respond with
     * <code>AclProperty</code>
     */
    public AclProperty aclfindMethod() throws HttpException, IOException {
        thisResource = true;
        return aclfindMethod(httpURL.getPath());
    }

    /**
     * Return the <code>AclProperty</code> for the resource at the given path
     *
     * @param path the server relative path of the resource to request
     * @return acl property, null if the server doesn't respond with
     * <code>AclProperty</code>
     */
    public AclProperty aclfindMethod(String path) throws HttpException, IOException {

        setClient();

        AclProperty acl = null;

        Vector properties = new Vector();
        properties.addElement(AclProperty.TAG_NAME);

        // Default depth=0, type=by_name
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), DepthSupport.DEPTH_0,
                properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Enumeration responses = method.getResponses();
        if (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                if (property instanceof AclProperty) {
                    acl = (AclProperty) property;
                }

            }
        }

        return acl;
    }

    /**
     * Get the <code>PrincipalCollectionSetProperty</code> for the current
     * resource.
     *
     * @return principal collection set Property, null if the server doesn't
     * respond with a <code>PrincipalCollectionSetProperty</code>
     */
    public PrincipalCollectionSetProperty principalCollectionSetFindMethod() throws HttpException, IOException {
        thisResource = true;
        return principalCollectionSetFindMethod(httpURL.getPath());
    }

    /**
     * Get the <code>PrincipalCollectionSetProperty</code> for the resource.
     *
     * @param path the server relative path of the resource to request
     * @return principal collection set Property, null if the server doesn't
     * respond with a <code>PrincipalCollectionSetProperty</code>
     */
    public PrincipalCollectionSetProperty principalCollectionSetFindMethod(String path)
            throws HttpException, IOException {

        setClient();

        PrincipalCollectionSetProperty set = null;

        Vector properties = new Vector();
        properties.addElement(PrincipalCollectionSetProperty.TAG_NAME);

        // Default depth=0, type=by_name
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), DepthSupport.DEPTH_0,
                properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Enumeration responses = method.getResponses();
        if (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                if (property instanceof PrincipalCollectionSetProperty) {
                    set = (PrincipalCollectionSetProperty) property;
                }

            }
        }

        return set;
    }

    /**
     * Return the LockDiscoveryProperty for the current resource
     *
     * @return null if the server doesn't respond with a LockDiscoveryProperty
     */
    public LockDiscoveryProperty lockDiscoveryPropertyFindMethod() throws HttpException, IOException {
        thisResource = true;
        return lockDiscoveryPropertyFindMethod(httpURL.getPath());
    }

    /**
     * Return the LockDiscoveryProperty for the resource at the given path
     *
     * @param path the server relative path of the resource to request
     * @return null if the server doesn't respond with a LockDiscoveryProperty
     */
    public LockDiscoveryProperty lockDiscoveryPropertyFindMethod(String path) throws HttpException, IOException {

        setClient();

        LockDiscoveryProperty set = null;

        Vector properties = new Vector();
        properties.addElement(LockDiscoveryProperty.TAG_NAME);

        // Default depth=0, type=by_name
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), DepthSupport.DEPTH_0,
                properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Enumeration responses = method.getResponses();
        if (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                if (property instanceof LockDiscoveryProperty) {
                    set = (LockDiscoveryProperty) property;
                }

            }
        }

        return set;
    }

    /**
     * Get InputStream for the GET method.
     *
     * @return InputStream
     * @exception HttpException
     * @exception IOException
     */
    public InputStream getMethodData() throws HttpException, IOException {

        return getMethodData(httpURL.getPathQuery());
    }

    /**
     * Get InputStream for the GET method for the given path.
     *
     * @param path the server relative path of the resource to get
     * @return InputStream
     * @exception HttpException
     * @exception IOException
     */
    public InputStream getMethodData(String path) throws HttpException, IOException {

        setClient();

        GetMethod method = new GetMethod(URIUtil.encodePathQuery(path));

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        int statusCode = method.getStatusLine().getStatusCode();
        setStatusCode(statusCode);

        if (statusCode >= 200 && statusCode < 300)
            return method.getResponseBodyAsStream();
        else
            throw new IOException("Couldn't get file");
    }

    /**
     * Get data as a String for the GET method.
     *
     * @return the contents of this resource as a string
     * @exception HttpException
     * @exception IOException
     */
    public String getMethodDataAsString() throws HttpException, IOException {

        return getMethodDataAsString(httpURL.getPathQuery());
    }

    /**
     * Get data as a String for the GET method for the given path.
     *
     * @param path the server relative path of the resource to get
     * @return the contents of the given resource as a string
     * @exception HttpException
     * @exception IOException
     */
    public String getMethodDataAsString(String path) throws HttpException, IOException {

        setClient();
        GetMethod method = new GetMethod(URIUtil.encodePathQuery(path));
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return method.getResponseBodyAsString();
    }

    /**
     * Execute the GET method for this WebdavResource path.
     *
     * @param file The local file.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean getMethod(File file) throws HttpException, IOException {

        return getMethod(httpURL.getPathQuery(), file);
    }

    /**
     * Execute the GET method for the given path.
     *
     * @param path the server relative path of the resource to get
     * @param file The local file.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean getMethod(String path, File file) throws HttpException, IOException {

        setClient();
        GetMethod method = new GetMethod(URIUtil.encodePathQuery(path));

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        // get the file only if status is any kind of OK
        if (statusCode >= 200 && statusCode < 300) {

            // Do a simple little loop to read the response back into the passed
            // file parameter.
            InputStream inStream = method.getResponseBodyAsStream();

            FileOutputStream fos = new FileOutputStream(file);
            byte buffer[] = new byte[65535];
            int bytesRead;
            while ((bytesRead = inStream.read(buffer)) >= 0) {
                fos.write(buffer, 0, bytesRead);
            }
            inStream.close();
            fos.close();

            return true;

        } else {
            return false;

        }

    }

    /**
     * Execute the PUT method for this resource.
     *
     * @param data The byte array.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(byte[] data) throws HttpException, IOException {

        boolean result = putMethod(httpURL.getPathQuery(), data);
        if (result)
            refresh();
        return result;
    }

    /**
     * Execute the PUT method for the given path.
     *
     * @param path the server relative path to put the data
     * @param data The byte array.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String path, byte[] data) throws HttpException, IOException {

        setClient();
        PutMethod method = new PutMethod(URIUtil.encodePathQuery(path));
        generateIfHeader(method);
        if (getGetContentType() != null && !getGetContentType().equals(""))
            method.setRequestHeader("Content-Type", getGetContentType());

        method.setRequestHeader("Content-Length", String.valueOf(data.length));
        method.setRequestBody(new ByteArrayInputStream(data));

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the PUT method for this resource.
     *
     * @param is The input stream.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(InputStream is) throws HttpException, IOException {

        return putMethod(httpURL.getPathQuery(), is);
    }

    /**
     * Execute the PUT method for the given path.
     *
     * @param path the server relative path to put the data
     * @param is The input stream.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String path, InputStream is) throws HttpException, IOException {

        setClient();
        PutMethod method = new PutMethod(URIUtil.encodePathQuery(path));
        generateIfHeader(method);
        if (getGetContentType() != null && !getGetContentType().equals(""))
            method.setRequestHeader("Content-Type", getGetContentType());
        method.setRequestContentLength(PutMethod.CONTENT_LENGTH_CHUNKED);
        method.setRequestBody(is);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the PUT method for this WebdavResource.
     *
     * @param data String</cdoe> data to send.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String data) throws HttpException, IOException {

        boolean result = putMethod(httpURL.getPathQuery(), data);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the PUT method for the given path.
     *
     * @param path the server relative path to put the data
     * @param data String to send.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String path, String data) throws HttpException, IOException {

        setClient();
        PutMethod method = new PutMethod(URIUtil.encodePathQuery(path));
        generateIfHeader(method);
        if (getGetContentType() != null && !getGetContentType().equals(""))
            method.setRequestHeader("Content-Type", getGetContentType());
        method.setRequestBody(data);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the PUT method for this WebdavResource.
     *
     * @param file the filename to get on local.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(File file) throws HttpException, IOException {

        boolean result = putMethod(httpURL.getPathQuery(), file);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the PUT method for the given path.
     *
     * @param path the server relative path to put the given file
     * @param file the filename to get on local.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String path, File file) throws HttpException, IOException {

        setClient();
        PutMethod method = new PutMethod(URIUtil.encodePathQuery(path));
        generateIfHeader(method);
        if (getGetContentType() != null && !getGetContentType().equals(""))
            method.setRequestHeader("Content-Type", getGetContentType());
        long fileLength = file.length();
        method.setRequestContentLength(
                fileLength <= Integer.MAX_VALUE ? (int) fileLength : PutMethod.CONTENT_LENGTH_CHUNKED);
        method.setRequestBody(new FileInputStream(file));
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the PUT method for this resource from the given url.
     * It's like a streaming copy about a resource of the specified remote url
     * to another remote url of this resource.
     *
     * @param url The URL to get a resource.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(URL url) throws HttpException, IOException {

        boolean result = putMethod(httpURL.getPathQuery(), url);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the PUT method for the given path from the given url.
     *
     * @param path the server relative path to put the data
     * @param url The URL to get a resource.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean putMethod(String path, URL url) throws HttpException, IOException {

        setClient();
        PutMethod method = new PutMethod(URIUtil.encodePathQuery(path));
        generateIfHeader(method);
        if (getGetContentType() != null && !getGetContentType().equals(""))
            method.setRequestHeader("Content-Type", getGetContentType());
        method.setRequestBody(url.openStream());
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute OPTIONS method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean optionsMethod() throws HttpException, IOException {

        return optionsMethod(httpURL.getPath());
    }

    /**
     * Execute OPTIONS method for the given path.
     *
     * @param path the server relative path of the resource to request
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @see #getAllowedMethods()
     */
    public boolean optionsMethod(String path) throws HttpException, IOException {

        setClient();
        OptionsMethod method;
        if (path.trim().equals("*"))
            method = new OptionsMethod("*");
        else
            method = new OptionsMethod(URIUtil.encodePath(path));

        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        if (statusCode >= 200 && statusCode < 300) {
            // check if the specific method is possbile
            allowedMethods = method.getAllowedMethods();
            // check WebDAV capabilities.
            davCapabilities = method.getDavCapabilities();
            return true;
        }

        return false;
    }

    /**
     * Execute OPTIONS method for the given path.
     *
     * @param path the server relative path to send the request
     * @param aMethod a method to check it's supported.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean optionsMethod(String path, String aMethod) throws HttpException, IOException {

        if (aMethod != null && optionsMethod(path)) {
            while (allowedMethods.hasMoreElements()) {
                if (aMethod.equalsIgnoreCase((String) allowedMethods.nextElement()))
                    return true;
            }
        }

        return false;
    }

    /**
     * Execute OPTIONS method for the given http URL.
     *
     * @param httpURL the http URL.
     * @return the allowed methods and capabilities.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration optionsMethod(HttpURL httpURL) throws HttpException, IOException {

        HttpClient client = getSessionInstance(httpURL, true);

        OptionsMethod method = new OptionsMethod(httpURL.getEscapedPath());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector options = new Vector();
        int statusCode = method.getStatusLine().getStatusCode();
        if (statusCode >= 200 && statusCode < 300) {
            // check if the specific method is possbile
            Enumeration allowedMethods = method.getAllowedMethods();
            while (allowedMethods.hasMoreElements()) {
                options.addElement(allowedMethods.nextElement());
            }
            // check WebDAV capabilities.
            Enumeration davCapabilities = method.getDavCapabilities();
            while (davCapabilities.hasMoreElements()) {
                options.addElement(davCapabilities.nextElement());
            }
            Enumeration responses = method.getResponses();
            if (responses.hasMoreElements()) {
                ResponseEntity response = (ResponseEntity) responses.nextElement();
                Enumeration workspaces = response.getWorkspaces();
                String sResult = "";
                while (workspaces.hasMoreElements()) {
                    sResult += workspaces.nextElement().toString();
                }
                Enumeration histories = response.getHistories();
                while (histories.hasMoreElements()) {
                    sResult += histories.nextElement().toString();
                }
                // Set status code for this resource.
                if ((thisResource == true) && (response.getStatusCode() > 0))
                    setStatusCode(response.getStatusCode());
                thisResource = false;
                options.addElement(sResult);
            }
        }

        return options.elements();
    }

    /**
     * Execute OPTIONS method for the given http URL, DELTAV
     *
     * @param httpURL the http URL.
     * @return the allowed methods and capabilities.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration optionsMethod(HttpURL httpURL, int type) throws HttpException, IOException {

        HttpClient client = getSessionInstance(httpURL, true);

        OptionsMethod method = new OptionsMethod(httpURL.getEscapedPath(), type);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector options = new Vector();
        int statusCode = method.getStatusLine().getStatusCode();
        if (statusCode >= 200 && statusCode < 300) {
            Enumeration responses = method.getResponses();
            if (responses.hasMoreElements()) {
                ResponseEntity response = (ResponseEntity) responses.nextElement();
                // String sResult="";
                if (type == OPTIONS_WORKSPACE) {
                    Enumeration workspaces = response.getWorkspaces();
                    while (workspaces.hasMoreElements()) {
                        options.add(workspaces.nextElement().toString());
                    }
                } else if (type == OPTIONS_VERSION_HISTORY) {
                    Enumeration histories = response.getHistories();
                    while (histories.hasMoreElements()) {
                        options.add(histories.nextElement().toString());
                    }
                }

                // Set status code for this resource.
                if ((thisResource == true) && (response.getStatusCode() > 0))
                    setStatusCode(response.getStatusCode());
                thisResource = false;
                // options.addElement(sResult);
            }
        }

        return options.elements();
    }

    /**
     * Execute OPTIONS method for the given path.
     *
     * @param path the server relative path of the resource to request
     * @return the allowed methods and capabilities.
     * @exception HttpException
     * @exception IOException
     * @see #getAllowedMethods()
     */
    public Enumeration optionsMethod(String path, int type) throws HttpException, IOException {

        setClient();

        OptionsMethod method = new OptionsMethod(URIUtil.encodePath(path), type);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector options = new Vector();
        int statusCode = method.getStatusLine().getStatusCode();
        if (statusCode >= 200 && statusCode < 300) {
            Enumeration responses = method.getResponses();
            if (responses.hasMoreElements()) {
                ResponseEntity response = (ResponseEntity) responses.nextElement();
                // String sResult="";
                if (type == OPTIONS_WORKSPACE) {
                    Enumeration workspaces = response.getWorkspaces();
                    while (workspaces.hasMoreElements()) {
                        options.add(workspaces.nextElement().toString());
                    }
                } else if (type == OPTIONS_VERSION_HISTORY) {
                    Enumeration histories = response.getHistories();
                    while (histories.hasMoreElements()) {
                        options.add(histories.nextElement().toString());
                    }
                }

                // Set status code for this resource.
                if ((thisResource == true) && (response.getStatusCode() > 0))
                    setStatusCode(response.getStatusCode());
                thisResource = false;
                // options.addElement(sResult);
            }
        }

        return options.elements();
    }

    /**
     * Execute a LABEL method on the current path, setting the given label
     *
     * @param labelname the label to set
     * @param type the type of action. One of:
     *        <ul>
     *        <li> LABEL_ADD
     *        <li> LABEL_REMOVE
     *        <li> LABEL_SET
     *        </ul>
     * @return true if the method succeeded
     */
    public boolean labelMethod(String labelname, int type) throws HttpException, IOException {
        return labelMethod(httpURL.getPath(), labelname, type);
    }

    /**
     * Execute a LABEL method on the given path, setting the given label
     *
     * @param path the server relative path of the resource to act on
     * @param labelname the label to set
     * @param type the type of action. One of:
     *        <ul>
     *        <li> LABEL_ADD
     *        <li> LABEL_REMOVE
     *        <li> LABEL_SET
     *        </ul>
     * @return true if the method succeeded
     */
    public boolean labelMethod(String path, String labelname, int type) throws HttpException, IOException {
        int labeltype = 0;

        switch (type) {
        case LABEL_SET:
            labeltype = LabelMethod.LABEL_SET;
            break;
        case LABEL_REMOVE:
            labeltype = LabelMethod.LABEL_REMOVE;
            break;
        case LABEL_ADD:
            labeltype = LabelMethod.LABEL_ADD;
            break;
        }

        setClient();
        LabelMethod method = new LabelMethod(URIUtil.encodePath(path), labeltype, labelname);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the REPORT method.
     */
    public Enumeration reportMethod(HttpURL httpURL, int depth)

            throws HttpException, IOException {
        setClient();
        // Default depth=0, type=by_name
        ReportMethod method = new ReportMethod(httpURL.getEscapedPath(), depth);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector results = new Vector();

        Enumeration responses = method.getResponses();
        while (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();
            String sResult = href;

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                sResult += "\n" + property.getName() + ":\t" + DOMUtils.getTextValue(property.getElement());

            }
            results.addElement(sResult);
        }

        return results.elements();
    }

    public Enumeration reportMethod(HttpURL httpURL, Vector properties)

            throws HttpException, IOException {
        setClient();
        // Default depth=0, type=by_name
        ReportMethod method = new ReportMethod(httpURL.getEscapedPath(), DepthSupport.DEPTH_0,
                properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        return method.getResponses();
    }

    public Enumeration reportMethod(HttpURL httpURL, Vector properties, int depth)

            throws HttpException, IOException {
        setClient();
        // Default depth=0, type=by_name
        ReportMethod method = new ReportMethod(httpURL.getEscapedPath(), depth, properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        /*first draft, does work anyhow
         Enumeration results = method.getAllResponseURLs();
            
         return results;*/
        /*  Enumeration responses = method.getResponses();
         ResponseEntity response = (ResponseEntity) responses.nextElement();
         String href = (String) response.getHref();
         Enumeration results = method.getResponseProperties(href);
            
         return results;*/

        Vector results = new Vector();

        Enumeration responses = method.getResponses();
        while (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();
            String sResult = href;

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                sResult += "\n" + property.getName() + ":\t" + DOMUtils.getTextValue(property.getElement());
                // results.addElement(DOMUtils.getTextValue(property.getElement()));
            }
            results.addElement(sResult);
        }

        return results.elements();
    }

    // locate-by-history Report
    public Enumeration reportMethod(HttpURL httpURL, Vector properties, Vector histUri, int depth)

            throws HttpException, IOException {
        setClient();
        // Default depth=0, type=by_name
        ReportMethod method = new ReportMethod(httpURL.getEscapedPath(), depth, properties.elements(),
                histUri.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector results = new Vector();

        Enumeration responses = method.getResponses();
        while (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();
            String sResult = href;

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                sResult += "\n" + property.getName() + ":\t" + DOMUtils.getTextValue(property.getElement());
            }
            results.addElement(sResult);
        }

        return results.elements();
    }

    // expand-property Report
    public Enumeration reportMethod(HttpURL httpURL, String sQuery, int depth)

            throws HttpException, IOException {
        setClient();
        // Default depth=0, type=by_name
        ReportMethod method = new ReportMethod(httpURL.getEscapedPath(), depth, sQuery);

        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        client.executeMethod(method);

        Vector results = new Vector();

        Enumeration responses = method.getResponses();
        while (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            //String href = (String) response.getHref();
            String sResult; //= href;

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            sResult = response.toString();
            /*while (responseProperties.hasMoreElements()) {
             Property property = (Property) responseProperties.nextElement();
             sResult += "\t" + DOMUtils.getTextValue(property.getElement());
                
             }*/
            results.addElement(sResult);
        }

        return results.elements();
    }

    /**
     * Execute PROPFIND method with allprop for this WebdavResource.
     * Get list of all WebDAV properties on this WebDAV resource.
     *
     * <p>Once used this method, the the status code in the 207
     * reponse is need to be set for the method of WebdavResource.
     *
     * <p>The values of DepthSupport.DEPTH_0, DepthSupport.DEPTH_1,
     * DepthSupport.DEPTH_INFINITY is possbile for the depth.
     *
     * @param depth
     * @return an enumeration of <code>ResponseEntity</code>
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(int depth) throws HttpException, IOException {

        thisResource = true;
        return propfindMethod(httpURL.getPath(), depth);
    }

    /**
     * Execute PROPFIND method with allprop for the given path.
     * Get list of all WebDAV properties on the given resource.
     *
     * <p>Once used this method, the the status code in the 207
     * reponse is need to be set for the method of WebdavResource.
     *
     * <p>The values of DepthSupport.DEPTH_0, DepthSupport.DEPTH_1,
     * DepthSupport.DEPTH_INFINITY is possbile for the depth.
     *
     * @param path the server relative path of the resource to request
     * @param depth
     * @return an enumeration of <code>ResponseEntity</code>
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(String path, int depth) throws HttpException, IOException {

        setClient();
        // Change the depth for allprop
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), depth);

        method.setDebug(debug);

        // Default depth=infinity, type=allprop
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int status = client.executeMethod(method);

        // Set status code for this resource.
        if (thisResource == true) {
            setStatusCode(status);
        }
        // Also accept OK sent by buggy servers.
        if (status != HttpStatus.SC_MULTI_STATUS && status != HttpStatus.SC_OK) {
            HttpException ex = new HttpException();
            ex.setReasonCode(status);
            throw ex;
        }
        thisResource = false;

        return method.getResponses();
    }

    /**
     * Execute PROPFIND method with by propname for this resource.
     * Get list of named WebDAV properties on this resource.
     *
     * <p>Once used this method, the the status code in the 207
     * reponse is need to be set for the method of WebdavResource.
     *
     * <p>The values of DepthSupport.DEPTH_0, DepthSupport.DEPTH_1
     * DepthSupport.DEPTH_INFINITY is possbile for the depth.
     *
     * @param depth The depth.
     * @param properties The named properties.
     * @return an enumeration of <code>ResponseEntity</code>
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(int depth, Vector properties) throws HttpException, IOException {

        thisResource = true;
        return propfindMethod(httpURL.getPath(), depth, properties);
    }

    /**
     * Execute PROPFIND method with by propname for the given path.
     * Get list of named WebDAV properties on the given resource.
     *
     * <p>Once used this method, the the status code in the 207
     * reponse is need to be set for the method of WebdavResource.
     *
     * <p>The values of DepthSupport.DEPTH_0, DepthSupport.DEPTH_1
     * DepthSupport.DEPTH_INFINITY is possbile for the depth.
     *
     * @param path the server relative path of the resource to request
     * @param depth The depth.
     * @param properties The named properties.
     * @return an enumeration of <code>ResponseEntity</code>
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(String path, int depth, Vector properties) throws HttpException, IOException {

        setClient();
        // Change the depth for prop
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), depth, properties.elements());

        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int status = client.executeMethod(method);

        // Set status code for this resource.
        if (thisResource == true) {
            // Set the status code.
            setStatusCode(method.getStatusLine().getStatusCode());
        }
        // Also accept OK sent by buggy servers.
        if (status != HttpStatus.SC_MULTI_STATUS && status != HttpStatus.SC_OK) {
            HttpException ex = new HttpException();
            ex.setReasonCode(status);
            throw ex;
        }
        thisResource = false;

        return method.getResponses();
    }

    /**
     * Execute PROPFIND method for this WebdavResource.
     * Get list of given WebDAV properties on this WebDAV resource.
     *
     * @param propertyName the WebDAV property to find.
     * @return Enumeration list of WebDAV properties on a resource.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(String propertyName) throws HttpException, IOException {

        Vector property = new Vector();
        property.addElement(propertyName);

        thisResource = true;
        return propfindMethod(httpURL.getPath(), property);
    }

    /**
     * Execute PROPFIND method for the given WebdavResource path.
     * Get list of given WebDAV properties on this WebDAV resource.
     *
     * @param path the server relative path of the resource to request
     * @param propertyName the WebDAV property to find.
     * @return Enumeration list of WebDAV properties on a resource.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(String path, String propertyName) throws HttpException, IOException {

        Vector property = new Vector();
        property.addElement(propertyName);

        thisResource = false;
        return propfindMethod(path, property);
    }

    /**
     * Execute PROPFIND method for this WebdavResource.
     * Get list of given WebDAV properties on this WebDAV resource.
     *
     * @param properties the WebDAV properties to find.
     * @return Enumeration list of WebDAV properties on a resource.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(Vector properties) throws HttpException, IOException {

        thisResource = true;
        return propfindMethod(httpURL.getPath(), properties);
    }

    /**
     * Execute PROPFIND method for the given path and properties.
     * Get list of given WebDAV properties on the given resource.
     *
     * @param path the server relative path of the resource to request
     * @param properties the WebDAV properties to find.
     * @return Enumeration list of WebDAV properties on a resource.
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration propfindMethod(String path, Vector properties) throws HttpException, IOException {

        setClient();
        // Default depth=0, type=by_name
        PropFindMethod method = new PropFindMethod(URIUtil.encodePath(path), DepthSupport.DEPTH_0,
                properties.elements());
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int status = client.executeMethod(method);

        // Also accept OK sent by buggy servers.
        if (status != HttpStatus.SC_MULTI_STATUS && status != HttpStatus.SC_OK) {
            HttpException ex = new HttpException();
            ex.setReasonCode(status);
            throw ex;
        }

        // It contains the results.
        Vector results = new Vector();

        Enumeration responses = method.getResponses();
        if (responses.hasMoreElements()) {
            ResponseEntity response = (ResponseEntity) responses.nextElement();
            String href = response.getHref();

            // Set status code for this resource.
            if ((thisResource == true) && (response.getStatusCode() > 0))
                setStatusCode(response.getStatusCode());
            thisResource = false;

            Enumeration responseProperties = method.getResponseProperties(href);
            while (responseProperties.hasMoreElements()) {
                Property property = (Property) responseProperties.nextElement();
                results.addElement(property.getPropertyAsString());
            }
        }

        return results.elements();
    }

    /**
     * Execute PROPATCH method for this WebdavResource.
     *
     * @param propertyName the name of the property to set
     * @param propertyValue the value of the property to set
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(String propertyName, String propertyValue) throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), propertyName, propertyValue, true);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for this resource with the given property.
     *
     * @param propertyName the property name string (in "DAV:" namespace)
     * @param propertyValue the property value string
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's being set, false if it's being removed
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(String propertyName, String propertyValue, boolean action)
            throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), propertyName, propertyValue, action);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for this WebdavResource.
     *
     * @param propertyName the name of the property to set
     * @param propertyValue the value of the property to set
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(PropertyName propertyName, String propertyValue)
            throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), propertyName, propertyValue, true);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for this resource with the given property.
     *
     * @param propertyName the name of the property to set
     * @param propertyValue the value of the property to set
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's being set, false if it's being removed
     * @return true if the method is succeeded
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(PropertyName propertyName, String propertyValue, boolean action)
            throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), propertyName, propertyValue, action);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for the given WebdavResource.
     *
     * @param path the server relative path of the resource to act on
     * @param propertyName the property name in "DAV:" namespace
     * @param propertyValue the property value string
     * @return true if the method is succeeded
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(String path, String propertyName, String propertyValue)
            throws HttpException, IOException {

        Hashtable property = new Hashtable();
        property.put(propertyName, propertyValue);
        return proppatchMethod(path, property, true);
    }

    /**
     * Execute PROPATCH method for the specified resource with the given
     * property.
     *
     * @param path the server relative path of the resource to act on
     * @param propertyName the property name string (in "DAV:" namespace)
     * @param propertyValue the property value string
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's to be set, false if it's to be removed
     * @return true if the method is succeeded
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(String path, String propertyName, String propertyValue, boolean action)
            throws HttpException, IOException {

        Hashtable property = new Hashtable();
        property.put(propertyName, propertyValue);
        return proppatchMethod(path, property, action);
    }

    /**
     * Execute PROPATCH method for the given WebdavResource.
     *
     * @param path the server relative path of the resource to act on
     * @param propertyName the property name.
     * @param propertyValue the property value.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(String path, PropertyName propertyName, String propertyValue)
            throws HttpException, IOException {

        Hashtable property = new Hashtable();
        property.put(propertyName, propertyValue);

        return proppatchMethod(path, property, true);
    }

    /**
     * Execute PROPATCH method for the given resource with the given
     * properties.
     *
     * @param path the server relative path of the resource to act on
     * @param propertyName the property name
     * @param propertyValue the property value string
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's to be set, false if it's to be removed
     * @return true if the method is succeeded
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(String path, PropertyName propertyName, String propertyValue, boolean action)
            throws HttpException, IOException {

        Hashtable property = new Hashtable();
        property.put(propertyName, propertyValue);
        return proppatchMethod(path, property, action);
    }

    /**
     * Execute PROPATCH method for this WebdavResource.
     *
     * @param properties name and value pairs to set
     * (name can be a String or PropertyName)
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(Hashtable properties) throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), properties, true);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for this resource with the given properties.
     *
     * @param properties the name(= <code>String</code> or <code>PropertyName
     * </code> and value(= <code>String</code>) pairs for proppatch action
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's being set, false if it's being removed
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(Hashtable properties, boolean action) throws HttpException, IOException {

        boolean result = proppatchMethod(httpURL.getPath(), properties, action);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute PROPATCH method for the given WebdavResource.
     *
     * @param path the server relative path of the resource to act on
     * @param properties name and value pairs to set
     * (name can be a String or PropertyName)
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @deprecated it could be removed after the major version changes
     */
    public boolean proppatchMethod(String path, Hashtable properties) throws HttpException, IOException {

        return proppatchMethod(path, properties, true);
    }

    /**
     * Execute PROPATCH method for the specified resource with the given
     * properties.
     *
     * @param path the server relative path of the resource to act on
     * @param properties the name(= <code>String</code> or <code>PropertyName
     * </code> and value(= <code>String</code>) pairs for proppatch action
     * If the proppatch action is being removed, the value is null or any.
     * @param action true if it's being set, false if it's being removed
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean proppatchMethod(String path, Hashtable properties, boolean action)
            throws HttpException, IOException {

        setClient();
        PropPatchMethod method = new PropPatchMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        Enumeration names = properties.keys();
        boolean hasSomething = false;
        if (names.hasMoreElements()) {
            hasSomething = true;
        }
        while (names.hasMoreElements()) {
            Object item = names.nextElement();
            if (item instanceof String) {
                String name = (String) item;
                String value = (String) properties.get(item);
                if (action) {
                    method.addPropertyToSet(name, value);
                } else {
                    method.addPropertyToRemove(name);
                }
            } else if (item instanceof PropertyName) {
                String name = ((PropertyName) item).getLocalName();
                String namespaceURI = ((PropertyName) item).getNamespaceURI();
                String value = (String) properties.get(item);
                if (action) {
                    method.addPropertyToSet(name, value, null, namespaceURI);
                } else {
                    method.addPropertyToRemove(name, null, namespaceURI);
                }
            } else {
                // unknown type, debug or ignore it
            }
        }
        if (hasSomething) {
            generateTransactionHeader(method);
            generateAdditionalHeaders(method);
            int statusCode = client.executeMethod(method);
            // Possbile Status Codes => SC_OK
            // WebdavStatus.SC_FORBIDDEN, SC_CONFLICT, SC_LOCKED, 507
            setStatusCode(statusCode);
            if (statusCode >= 200 && statusCode < 300) {
                return true;
            }
        }
        return false;
    }

    /**
     * Execute the HEAD method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean headMethod() throws HttpException, IOException {

        return headMethod(httpURL.getPathQuery());
    }

    /**
     * Execute the HEAD method for the given path.
     *
     * @param path the server relative path of the resource to request
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean headMethod(String path) throws HttpException, IOException {

        setClient();
        HeadMethod method = new HeadMethod(URIUtil.encodePathQuery(path));

        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the DELETE method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean deleteMethod() throws HttpException, IOException {

        boolean result = deleteMethod(httpURL.getPath());
        if (result) {
            setExistence(false);
        }

        return result;
    }

    /**
     * Execute the DELETE method for the given path.
     *
     * @param path the server relative path of the resource to delete
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean deleteMethod(String path) throws HttpException, IOException {

        setClient();
        DeleteMethod method = new DeleteMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the MOVE method for this WebdavReource.
     *
     * @param destination the destination to move to as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean moveMethod(String destination) throws HttpException, IOException {

        boolean result = moveMethod(httpURL.getPath(), destination);
        if (result) {
            httpURL.setPath(destination);
            refresh();
        }

        return result;
    }

    /**
     * Execute the MOVE method for the given source and destination.
     *
     * @param source the source resource as a server relativ path
     * @param destination the destination to move to as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean moveMethod(String source, String destination) throws HttpException, IOException {

        setClient();
        MoveMethod method = new MoveMethod(URIUtil.encodePath(source), URIUtil.encodePath(destination));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateIfHeader(method);
        method.setOverwrite(overwrite);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile MOVE Status Codes => SC_CREATED, SC_NO_CONTENT
        // WebdavStatus.SC_FORBIDDEN, SC_CONFLICT, SC_PRECONDITION_FAILED,
        // SC_LOCKED, SC_BAD_GATEWAY
        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the COPY method for the given destination path.
     *
     * @param destination the destination as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean copyMethod(String destination) throws HttpException, IOException {

        boolean result = copyMethod(httpURL.getPath(), destination);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the COPY method the given source and destination.
     *
     * @param source the source resource as a server relative path
     * @param destination the destination as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean copyMethod(String source, String destination) throws HttpException, IOException {

        setClient();
        CopyMethod method = new CopyMethod(URIUtil.encodePath(source), URIUtil.encodePath(destination));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        method.setOverwrite(overwrite);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile COPY Status Codes => SC_CREATED, SC_NO_CONTENT
        // WebdavStatus.SC_FORBIDDEN, SC_CONFLICT, SC_PRECONDITION_FAILED,
        // SC_LOCKED, SC_BAD_GATEWAY, SC_INSUFFICIENT_STORAGE
        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the MKCOL method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean mkcolMethod() throws HttpException, IOException {

        boolean result = mkcolMethod(httpURL.getPath());
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the MKCOL method for the given path.
     *
     * @param path the server relative path at which to create a new collection
     *        resource
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean mkcolMethod(String path) throws HttpException, IOException {

        setClient();
        MkcolMethod method = new MkcolMethod(URIUtil.encodePath(path));

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile MKCOL Status Codes => SC_CREATED
        // WebdavStatus.SC_FORBIDDEN, SC_METHOD_NOT_ALLOWED, SC_CONFLICT,
        // SC_LOCKED, SC_UNSUPPORTED_MEDIA_TYPE, SC_INSUFFICIENT_STORAGE
        setStatusCode(statusCode);
        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the LOCK method for this WebdavResource. This method tries to
     * acquire an exclusive write lock with a timeout of 120 seconds.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod() throws HttpException, IOException {

        String owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;

        boolean result = lockMethod(httpURL.getPath(), owner, 120);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the LOCK method for this WebdavResource. This method tries to
     * acquire an exclusive write lock with the given timeout value.
     *
     * @param owner the owner string.
     * @param timeout the timeout
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod(String owner, int timeout) throws HttpException, IOException {

        boolean result = lockMethod(httpURL.getPath(), owner, timeout);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the LOCK method for the given path. This method tries to acquire
     * an exclusive write lock with a timeout of 120 seconds.
     *
     * @param path the server relative path of the resource to lock
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod(String path) throws HttpException, IOException {

        String owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;

        return lockMethod(path, owner, 120);
    }

    /**
     * Execute the LOCK method for the given path. This method tries to acquire
     * an exclusive write lock with the given timeout value.
     *
     * @param path the server relative path of the resource to lock
     * @param owner The owner string.
     * @param timeout the timeout value.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod(String path, String owner, int timeout) throws HttpException, IOException {

        return lockMethod(path, owner, timeout, LockMethod.SCOPE_EXCLUSIVE);
    }

    /**
     * Execute the LOCK method for the given path. This method tries to acquire
     * an exclusive write lock with the given timeout value.
     *
     * @param path the server relative path of the resource to lock
     * @param owner The owner string.
     * @param timeout the timeout value.
     * @param locktype, the scope of lock.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod(String path, String owner, int timeout, short lockType)
            throws HttpException, IOException {

        return lockMethod(path, owner, timeout, lockType, DepthSupport.DEPTH_INFINITY);
    }

    /**
     * Execute the LOCK method for the given path. This method tries to acquire
     * an exclusive write lock with the given timeout value.
     *
     * @param path the server relative path of the resource to lock
     * @param owner The owner string.
     * @param timeout the timeout value.
     * @param locktype, the scope of lock.
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean lockMethod(String path, String owner, int timeout, short lockType, int depth)
            throws HttpException, IOException {

        setClient();

        if (owner == null) {
            owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;
        }

        // default lock type setting
        LockMethod method = new LockMethod(URIUtil.encodePath(path), owner, lockType, timeout);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        method.setDepth(depth);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);
        String lock = method.getLockToken();
        WebdavState state = (WebdavState) client.getState();
        if (state != null) {
            state.addLock(path, lock);
        }
        this.owner = method.getOwner();

        // Possbile LOCK Status Codes => SC_OK
        // WebdavStatus.SC_SC_PRECONDITION_FAILED, SC_LOCKED
        setStatusCode(statusCode, lock);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the LOCK method for this WebdavResource.
     *
     * @see LockMethod
     * @deprecated The timeout value MUST NOT be greater than 2^32-1.
     */
    public boolean lockMethod(String owner, short timeout) throws HttpException, IOException {

        return lockMethod(owner, (int) timeout);
    }

    /**
     * Execute the LOCK method for the given path.
     *
     * @see LockMethod
     * @deprecated The timeout value MUST NOT be greater than 2^32-1.
     */
    public boolean lockMethod(String path, String owner, short timeout) throws HttpException, IOException {

        return lockMethod(path, owner, (int) timeout);
    }

    /**
     * Begins a new transaction. 
     * The transaction handle returned by the WebDAV server will be remembered and included
     * as a header of subsequent requests until either {@link #commitTransaction()} or {@link #abortTransaction()}
     * are called. You can retrieve it using {@link #getTransactionHandle()}.    
     * 
     * @param owner the owner of this transaction
     * @param timeout timeout in milleseconds
     * @return <code>true</code> if the transaction has been successfully started, <code>false</code> otherwise
     * @throws IOException if anything goes wrong
     * @see #commitTransaction()
     * @see #abortTransaction()
     * @see #getTransactionHandle()
     */
    public boolean startTransaction(String owner, int timeout) throws IOException {
        String path = httpURL.getPath();

        setClient();

        if (owner == null) {
            owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;
        }

        // default lock type setting
        LockMethod method = new LockMethod(path, owner, timeout, true);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);
        String txHandle = method.getLockToken();
        WebdavState state = (WebdavState) client.getState();
        if (state != null) {
            state.setTransactionHandle(txHandle);
        }
        this.owner = method.getOwner();

        // Possbile LOCK Status Codes => SC_OK
        // WebdavStatus.SC_SC_PRECONDITION_FAILED, SC_LOCKED
        setStatusCode(statusCode, txHandle);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Returns the transaction handle set by {@link #startTransaction(String, int)}. 
     * 
     * @return the current transaction handle or <code>null</code> if the client does not operate inside a transaction
     * @throws IOException if anything goes wrong
     * @see #startTransaction(String, int)
     */
    public String getTransactionHandle() throws IOException {
        setClient();

        // Get the lock for the given path.
        WebdavState state = (WebdavState) client.getState();
        if (state == null)
            return null;
        String txHandle = state.getTransactionHandle();
        return txHandle;
    }

    /**
     * Commits the transaction started by {@link #startTransaction(String, int)} and resets the transaction handle.
     *  
     * @return <code>true</code> if the transaction has been successfully committed, <code>false</code> otherwise
     * @throws IOException if anything goes wrong
     * @see #startTransaction(String, int)
     * @see #abortTransaction()
     * @see #getTransactionHandle()
     */
    public boolean commitTransaction() throws IOException {
        String path = httpURL.getPath();
        return endTransaction(path, UnlockMethod.COMMIT_TRANSACTION);
    }

    /**
     * Aborts - i.e. rolls back all changes of - the transaction started by {@link #startTransaction(String, int)} and resets the transaction handle.
     *  
     * @return <code>true</code> if the transaction has been successfully committed, <code>false</code> otherwise
     * @throws IOException if anything goes wrong
     * @see #startTransaction(String, int)
     * @see #abortTransaction()
     * @see #getTransactionHandle()
     */
    public boolean abortTransaction() throws IOException {
        String path = httpURL.getPath();
        return endTransaction(path, UnlockMethod.ABORT_TRANSACTION);
    }

    protected boolean endTransaction(String path, int transactionStatus) throws IOException {
        setClient();

        // Get the lock for the given path.
        WebdavState state = (WebdavState) client.getState();
        if (state == null)
            return false;
        String txHandle = state.getTransactionHandle();
        if (txHandle == null)
            return false;
        UnlockMethod method = new UnlockMethod(path, txHandle, transactionStatus);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        if (statusCode >= 200 && statusCode < 300) {
            state.setTransactionHandle(null);
            return true;
        }
        return false;
    }

    /**
     * Execute the Unlock method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean unlockMethod() throws HttpException, IOException {

        String owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;

        boolean result = unlockMethod(httpURL.getPath(), owner);
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the Unlock method for the given path.
     *
     * @param path the server relative path of the resource to unlock
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean unlockMethod(String path) throws HttpException, IOException {

        String owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;

        return unlockMethod(path, owner);
    }

    /**
     * Execute the Unlock method for the given path.
     *
     * @param path the server relative path of the resource to unlock
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean unlockMethod(String path, String owner) throws HttpException, IOException {

        setClient();

        if (owner == null) {
            owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;
        }

        // Get the lock for the given path.
        WebdavState state = (WebdavState) client.getState();
        // Discover the locktoken from the given lock owner

        state = discoverLock(owner, path, state);
        String lock = state.getLock(path);
        if (lock == null)
            return false;
        // unlock for the given path.
        UnlockMethod method = new UnlockMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        method.setLockToken(lock);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);
        if (statusCode >= 200 && statusCode < 300) {
            state.removeLocks(path);
            return true;
        }

        return false;
    }

    /**
     * Discover and refresh lock tokens.
     *
     * @exception HttpException
     * @exception IOException
     */
    public void discoverOwnLocks() throws HttpException, IOException {

        setClient();
        String owner = (httpURL.getUser() != null) ? httpURL.getUser() : defaultOwner;

        WebdavState state = (WebdavState) client.getState();
        state = discoverLock(owner, httpURL.getPath(), state);
        client.setState(state);
    }

    /**
     * Discover and refresh lock tokens for a specific owner.
     *
     * @param owner the owner who's locks are to be discovered.
     * @exception HttpException
     * @exception IOException
     */
    public void discoverOwnLocks(String owner) throws HttpException, IOException {

        setClient();

        WebdavState state = (WebdavState) client.getState();
        state = discoverLock(owner, httpURL.getPath(), state);
        client.setState(state);
    }

    /**
     * Discover the given owner and locktoken and set the locktoken
     *
     * @param owner the activelock owner
     * @param path the server relative path of the resource to request
     * @param state the state to save the locktoken
     * @return state probably having lock information renewly
     */
    protected WebdavState discoverLock(String owner, String path, WebdavState state) {
        try {
            lockDiscovery = lockDiscoveryPropertyFindMethod(path);
        } catch (Exception e) {
            return state;
        }

        if (lockDiscovery == null)
            return state;
        Lock[] activeLocks = lockDiscovery.getActiveLocks();

        if (activeLocks == null)
            return state;
        for (int i = 0; i < activeLocks.length; i++) {
            String activeLockOwner = activeLocks[i].getOwner();
            if (activeLockOwner.equals(owner)) {
                String locktoken = activeLocks[i].getLockToken();
                state.addLock(path, locktoken);
            }
        }
        return state;
    }

    /**
     * Update this resource to the specified target
     *
     * @param target the path of the history element to update this resource
     *        from
     * @return true if the method has succeeded
     * @exception HttpException
     * @exception IOException
     */
    public boolean updateMethod(String target) throws HttpException, IOException {

        return updateMethod(httpURL.getPath(), target);
    }

    /**
     * Update the specified resource to the specified target
     *
     * @param path the server relative path of the resource to update
     * @param target path of the target to update from (history resource)
     * @return true if the method has succeeded
     * @exception HttpException
     * @exception IOException
     */
    public boolean updateMethod(String path, String target) throws HttpException, IOException {

        setClient();
        UpdateMethod method = new UpdateMethod(URIUtil.encodePath(path), URIUtil.encodePath(target));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    public boolean versionControlMethod(String path) throws HttpException, IOException {

        setClient();

        VersionControlMethod method = new VersionControlMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    public boolean versionControlMethod(String path, String target) throws HttpException, IOException {

        setClient();

        VersionControlMethod method = new VersionControlMethod(URIUtil.encodePath(path),
                URIUtil.encodePath(target));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the MKWORKSPACE method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean mkWorkspaceMethod() throws HttpException, IOException {

        boolean result = mkWorkspaceMethod(httpURL.getPath());
        if (result)
            refresh();

        return result;
    }

    /**
     * Execute the MKCOL method for the given path.
     *
     * @param path the server relative path at which to create a new workspace
     *        resource
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean mkWorkspaceMethod(String path) throws HttpException, IOException {

        setClient();
        MkWorkspaceMethod method = new MkWorkspaceMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile MKCOL Status Codes => SC_CREATED
        // WebdavStatus.SC_FORBIDDEN, SC_METHOD_NOT_ALLOWED, SC_CONFLICT,
        // SC_LOCKED, SC_UNSUPPORTED_MEDIA_TYPE, SC_INSUFFICIENT_STORAGE

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    // -------------------------------------------------------- Basic Methods

    /**
     * Compare to the WebdavResource object.
     *
     * @param another the other WebdavResource object
     * @return the value 0 if the argument is equal.
     */
    public int compareToWebdavResource(WebdavResource another) {

        try {
            HttpURL anotherUrl = another.getHttpURL();

            String thisHost = httpURL.getHost();
            String anotherHost = anotherUrl.getHost();
            if (!thisHost.equalsIgnoreCase(anotherHost))
                return thisHost.compareTo(anotherHost);

            int thisPort = httpURL.getPort();
            int anotherPort = anotherUrl.getPort();
            if (thisPort != anotherPort)
                return (thisPort < anotherPort) ? -1 : 1;

            boolean thisCollection = isCollection();
            boolean anotherCollection = another.isCollection();
            if (thisCollection && !anotherCollection)
                return -1;
            if (anotherCollection && !thisCollection)
                return 1;

            String thisPath = httpURL.getPathQuery();
            String anotherPath = anotherUrl.getPathQuery();
            return thisPath.compareTo(anotherPath);
        } catch (Exception e) {
            // FIXME: not to return 0.
        }

        return 0;
    }

    /**
     * Compare to the given another object.
     *
     * @param another the other WebdavResource object
     * @return the value 0 if another is equal.
     */
    public int compareTo(Object another) {

        if ((another != null) && (another instanceof WebdavResource)) {
            return compareToWebdavResource((WebdavResource) another);
        }

        String thisUrl = toString();
        String anotherUrl = another.toString();

        return thisUrl.compareTo(anotherUrl);
    }

    /**
     * Test the object.
     *
     * @param obj the other object
     * @return true if it's equal.
     */
    public boolean equals(Object obj) {

        if ((obj != null) && (obj instanceof WebdavResource)) {
            return compareTo(obj) == 0;
        }
        return false;
    }

    /**
     * Return the http URL string.
     *
     * @return the http URL string.
     */
    public String toString() {
        return httpURL.toString();
    }

    /**
     * Execute the CHECKIN method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean checkinMethod() throws HttpException, IOException {

        return checkinMethod(httpURL.getPath());
    }

    /**
     * Execute the CHECKIN method for the given path.
     *
     * @param path the server relative path of the resource to check in
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean checkinMethod(String path) throws HttpException, IOException {

        setClient();
        CheckinMethod method = new CheckinMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the CHECKOUT method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean checkoutMethod() throws HttpException, IOException {

        return checkoutMethod(httpURL.getPath());
    }

    /**
     * Execute the CHECKOUT method for the given path.
     *
     * @param path the server relative path of the resource to check out
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean checkoutMethod(String path) throws HttpException, IOException {

        setClient();
        CheckoutMethod method = new CheckoutMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Execute the CHECKOUT method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean uncheckoutMethod() throws HttpException, IOException {

        return uncheckoutMethod(httpURL.getPath());
    }

    /**
     * Execute the CHECKOUT method for the given path.
     *
     * @param path the server relative path of the resource to act on
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean uncheckoutMethod(String path) throws HttpException, IOException {

        setClient();
        UncheckoutMethod method = new UncheckoutMethod(URIUtil.encodePath(path));
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        generateIfHeader(method);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        setStatusCode(statusCode);

        return (statusCode >= 200 && statusCode < 300) ? true : false;
    }

    /**
     * Create a new WebdavResource object (as a seperate method so that it can
     * be overridden by subclasses.
     *
     * @param client HttpClient to be used by this webdavresource.
     * @return A new WebdavResource object.
     */
    protected WebdavResource createWebdavResource(HttpClient client) {
        WebdavResource resource = new WebdavResource(client);
        resource.setProxy(proxyHost, proxyPort);
        resource.setProxyCredentials(proxyCredentials);
        return resource;
    }

    /**
     * Process a property, setting various member variables depending
     * on what the property is.
     *
     * @param property The property to process.
     */
    protected void processProperty(Property property) {
        if (property.getLocalName().equals(DISPLAYNAME)) {
            displayName = property.getPropertyAsString();
        } else if (property.getLocalName().equals(GETCONTENTLENGTH)) {
            String getContentLength = property.getPropertyAsString();
            setGetContentLength(getContentLength);
        } else if (property.getLocalName().equals(RESOURCETYPE)) {
            ResourceTypeProperty resourceType = (ResourceTypeProperty) property;
            setResourceType(resourceType);
        } else if (property.getLocalName().equals(GETCONTENTTYPE)) {
            String getContentType = property.getPropertyAsString();
            setGetContentType(getContentType);
        } else if (property.getLocalName().equals(GETLASTMODIFIED)) {
            String getLastModified = property.getPropertyAsString();
            setGetLastModified(getLastModified);
        } else if (property.getLocalName().equals(CREATIONDATE)) {
            String creationDate = property.getPropertyAsString();
            setCreationDate(creationDate);
        } else if (property.getLocalName().equals(GETETAG)) {
            String getEtag = property.getPropertyAsString();
            setGetEtag(getEtag);
        } else if (property.getLocalName().equals(ISHIDDEN)) {
            String isHidden = property.getPropertyAsString();
            setIsHidden(isHidden);
        } else if (property.getLocalName().equals(ISCOLLECTION)) {
            String isCollection = property.getPropertyAsString();
            setIsCollection(isCollection);
        } else if (property.getLocalName().equals(SUPPORTEDLOCK)) {
            String supportedLock = property.getPropertyAsString();
            setSupportedLock(supportedLock);
        } else if (property.getLocalName().equals(LOCKDISCOVERY)) {
            LockDiscoveryProperty lockDiscovery = (LockDiscoveryProperty) property;
            setLockDiscovery(lockDiscovery);
        }
    }

    /**
     * Execute REPORT method.
     * This method is for the special Access Control Reports:
     * - acl-principal-prop-set (not supported yet)
     * - principal-match (not supported yet)
     * - principal-property-search
     * - principal-search-property-set (not supported yet)
     *
     * @param path the server relative path of the resource to request
     * @param properties The named properties.
     * @return an enumeration of <code>ResponseEntity</code>
     * @exception HttpException
     * @exception IOException
     */
    public Enumeration aclReportMethod(String path, Collection properties, int reportType)
            throws HttpException, IOException {

        setClient();
        AclReportMethod method = new AclReportMethod(URIUtil.encodePath(path), properties, DepthSupport.DEPTH_0,
                reportType);

        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int status = client.executeMethod(method);

        // Set status code for this resource.
        if (thisResource == true) {
            // Set the status code.
            setStatusCode(method.getStatusLine().getStatusCode());
        }

        //slide/tamino delivers status code OK.
        //can be removed when the server sends MULTI_STATUS
        if (status != HttpStatus.SC_MULTI_STATUS && status != HttpStatus.SC_OK) {
            HttpException ex = new HttpException();
            ex.setReasonCode(status);
            throw ex;
        }
        thisResource = false;

        return method.getResponses();
    }

    /**
     * Execute the BIND method for this WebdavResource, given
     * an existing path to bind with.
     *
     * @param newBinding  the new binding as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @see #setOverwrite(boolean)
     */
    public boolean bindMethod(String newBinding) throws HttpException, IOException {
        return bindMethod(httpURL.getPath(), newBinding);
    }

    /**
     * Execute the BIND method given the new path to bind to an existing path.
     *
     * @param existingBinding  the existing binding as a server relative path
     * @param newBinding       the new binding as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @see #setOverwrite(boolean)
     */
    public boolean bindMethod(String existingBinding, String newBinding) throws HttpException, IOException {

        setClient();
        BindMethod method = new BindMethod(URIUtil.encodePath(existingBinding), URIUtil.encodePath(newBinding));
        method.setDebug(debug);
        method.setOverwrite(overwrite);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile BIND Status Codes => SC_CREATED, SC_NO_CONTENT
        // SC_FORBIDDEN, SC_CONFLICT, SC_PRECONDITION_FAILED,
        // SC_LOCKED, SC_BAD_GATEWAY, SC_INSUFFICIENT_STORAGE,
        // SC_LOOP_DETECTED
        setStatusCode(statusCode);
        return statusCode >= 200 && statusCode < 300;
    }

    /**
     * Execute the UNBIND method for this WebdavResource.
     *
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean unbindMethod() throws HttpException, IOException {
        boolean result = unbindMethod(httpURL.getPath());
        if (result) {
            setExistence(false);
        }

        return result;
    }

    /**
     * Execute the UNBIND method given the resource to Unbind.
     *
     * @param binding  the server relative path of the resource to unbind
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     */
    public boolean unbindMethod(String binding) throws HttpException, IOException {

        setClient();
        UnbindMethod method = new UnbindMethod(URIUtil.encodePath(binding));
        method.setDebug(debug);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile BIND Status Codes => SC_CREATED, SC_NOT_FOUND
        // WebdavStatus.SC_FORBIDDEN, SC_CONFLICT, SC_PRECONDITION_FAILED,
        // SC_LOCKED, SC_BAD_GATEWAY
        setStatusCode(statusCode);
        return statusCode >= 200 && statusCode < 300;
    }

    /**
     * Execute the Rebind method for this WebdavResource given the new
     * Resource to bind with.
     * The REBIND method removes a binding to a resource from one collection,
     * and adds a binding to that resource into another collection. It is
     * effectively an atomic form of a MOVE request.
     *
     * @param newBinding the new binding as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @see #setOverwrite(boolean)
     */
    public boolean rebindMethod(String newBinding) throws HttpException, IOException {
        boolean result = rebindMethod(httpURL.getPath(), newBinding);
        if (result) {
            httpURL.setPath(newBinding);
            refresh();
        }

        return result;
    }

    /**
     * Execute the Rebind method given a resource to rebind and the new
     * Resource to bind with.
     * The REBIND method removes a binding to a resource from one collection,
     * and adds a binding to that resource into another collection. It is
     * effectively an atomic form of a MOVE request
     *
     * @param existingBinding  the existing binding as a server relative path
     * @param newBinding       the new binding as a server relative path
     * @return true if the method is succeeded.
     * @exception HttpException
     * @exception IOException
     * @see #setOverwrite(boolean)
     */
    public boolean rebindMethod(String existingBinding, String newBinding) throws HttpException, IOException {

        setClient();
        RebindMethod method = new RebindMethod(URIUtil.encodePath(existingBinding), URIUtil.encodePath(newBinding));
        method.setDebug(debug);
        method.setOverwrite(overwrite);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);
        int statusCode = client.executeMethod(method);

        // Possbile BIND Status Codes => SC_CREATED, SC_NO_CONTENT
        // WebdavStatus.SC_FORBIDDEN, SC_CONFLICT, SC_PRECONDITION_FAILED,
        // SC_LOCKED, SC_BAD_GATEWAY, SC_INSUFFICIENT_STORAGE,
        // SC_LOOP_DETECTED
        setStatusCode(statusCode);
        return statusCode >= 200 && statusCode < 300;
    }

    /**
     * Subscribes for notifications for modifications of WebDAV resources.
     * 
     * @param path URL path of the resource that is to be subscribed
     * @param notificationType 
     * @param callback the URL to be registered for notification, may be 
     *        <code>null</code> if no callback shall be registered.
     * @param notificationDelay
     * @param depth the depth of the subscription (for valid values see 
     *    {@link DepthSupport})
     * @param lifetime duration of that subscription in seconds (Note: the
     *    server may change this and return an other one; 
     *    see {@link Subscription#getLifetime()}.
     * 
     * @return a {@link Subscription} or <code>null</code> if an error occurs
     * @throws HttpException
     * @throws IOException
     */
    public Subscription subscribeMethod(String path, String notificationType, String callback,
            long notificationDelay, int depth, long lifetime) throws HttpException, IOException {
        setClient();

        SubscribeMethod method = new SubscribeMethod(path);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        method.setCallback(callback);
        method.setDepth(depth);
        method.setSubsciptionLifetime(lifetime);
        method.setNotificationType(notificationType);
        method.setNotificationDelay(notificationDelay);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);

        int statusCode = client.executeMethod(method);

        if (statusCode == HttpStatus.SC_OK) {
            return new Subscription(path, method.getResponsedSubscriptionId(), method.getCallback(),
                    method.getResponsedSubscriptionLifetime(), method.getResponsedContentLocation(),
                    method.getNotificationType());
        } else {
            return null;
        }
    }

    /**
     * Refreshes a subscription.
     * 
     * @return <code>true</code> on success.
     */
    public boolean subscribeMethod(String path, int subscriptionId) throws HttpException, IOException {
        setClient();

        SubscribeMethod method = new SubscribeMethod(path);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        method.setSubscriptionId(subscriptionId);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);

        int statusCode = client.executeMethod(method);

        if (statusCode == HttpStatus.SC_OK) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Refreshes a subscription.
     * 
     * @param subscription The subscription to be refreshed.
     * @return <code>true</code> on success
     */
    public boolean subscribeMethod(Subscription subscription) throws HttpException, IOException {
        return subscribeMethod(subscription.getPath(), subscription.getId());
    }

    /**
     * Cancels a subscription.
     * @param path URL path for that was subscribed 
     * @return <code>true</code> on success
     */
    public boolean unsubscribeMethod(String path, int subscriptionId) throws HttpException, IOException {
        setClient();

        UnsubscribeMethod method = new UnsubscribeMethod(path);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        method.addSubscriptionId(subscriptionId);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);

        int statusCode = client.executeMethod(method);

        if (statusCode == HttpStatus.SC_OK) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Cancels a subscription.
     * @param subscription
     * @return <code>true</code> on success
     */
    public boolean unsubscribeMethod(Subscription subscription) throws HttpException, IOException {
        return unsubscribeMethod(subscription.getPath(), subscription.getId());
    }

    /**
     * Asks the server whether events for a given subscription are fired.
     * @param contentLocation URL path returned by the SUBSCRIBE methods
     *                        Content-Location header
     * @param subscriptionId id of the subscription
     * @return <code>true</code> if an event was fired
     */
    public boolean pollMethod(String contentLocation, int subscriptionId) throws HttpException, IOException {
        setClient();

        PollMethod method = new PollMethod(contentLocation);
        method.setDebug(debug);
        method.setFollowRedirects(this.followRedirects);

        method.addSubscriptionId(subscriptionId);
        generateTransactionHeader(method);
        generateAdditionalHeaders(method);

        int statusCode = client.executeMethod(method);

        if (statusCode == HttpStatus.SC_MULTI_STATUS) {
            return method.getSubscriptionsWithEvents().size() > 0;
        } else {
            return false;
        }
    }

    /**
     * Asks the server whether events for a given subscription are fired.
     * @param subscription the subscription to ask for
     * @return <code>true</code> if an event was fired
     */
    public boolean pollMethod(Subscription subscription) throws HttpException, IOException {
        return pollMethod(subscription.getContentLocation(), subscription.getId());
    }

    private static String getName(String uri, boolean decode) {
        String escapedName = URIUtil.getName(uri.endsWith("/") ? uri.substring(0, uri.length() - 1) : uri);
        if (decode) {
            try {
                return URIUtil.decode(escapedName);
            } catch (URIException e) {
                // Oh well
            }
        }
        return escapedName;
    }

    /**
     * Unescape octets for some characters that a server might (but should not)
     * have escaped. These are: "-", "_", ".", "!", "~", "*", "'", "(", ")"
     * Look at section 2.3 of RFC 2396.
     */
    private static String decodeMarks(String input) {
        char[] sequence = input.toCharArray();
        StringBuffer decoded = new StringBuffer(sequence.length);
        for (int i = 0; i < sequence.length; i++) {
            if (sequence[i] == '%' && i < sequence.length - 2) {
                switch (sequence[i + 1]) {
                case '2':
                    switch (sequence[i + 2]) {
                    case 'd':
                    case 'D':
                        decoded.append('-');
                        i += 2;
                        continue;
                    case 'e':
                    case 'E':
                        decoded.append('.');
                        i += 2;
                        continue;
                    case '1':
                        decoded.append('!');
                        i += 2;
                        continue;
                    case 'a':
                    case 'A':
                        decoded.append('*');
                        i += 2;
                        continue;
                    case '7':
                        decoded.append('\'');
                        i += 2;
                        continue;
                    case '8':
                        decoded.append('(');
                        i += 2;
                        continue;
                    case '9':
                        decoded.append(')');
                        i += 2;
                        continue;
                    }
                    break;
                case '5':
                    switch (sequence[i + 2]) {
                    case 'f':
                    case 'F':
                        decoded.append('_');
                        i += 2;
                        continue;
                    }
                    break;
                case '7':
                    switch (sequence[i + 2]) {
                    case 'e':
                    case 'E':
                        decoded.append('~');
                        i += 2;
                        continue;
                    }
                    break;
                }
            }
            decoded.append(sequence[i]);
        }
        return decoded.toString();
    }
}