jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r161660 [3/7] - in incubator/jackrabbit/trunk/contrib/jcr-server: ./ client/ client/src/java/org/apache/jackrabbit/webdav/ client/src/java/org/apache/jackrabbit/webdav/client/ client/src/java/org/apache/jackrabbit/webdav/client/methods/ server/ server/src/java/org/apache/jackrabbit/ server/src/java/org/apache/jackrabbit/server/ server/src/java/org/apache/jackrabbit/server/io/ server/src/java/org/apache/jackrabbit/server/jcr/ server/src/java/org/apache/jackrabbit/server/simple/ server/src/java/org/apache/jackrabbit/server/simple/dav/ server/src/java/org/apache/jackrabbit/webdav/simple/ server/src/java/org/apache/jackrabbit/webdav/spi/ server/src/java/org/apache/jackrabbit/webdav/spi/lock/ server/src/java/org/apache/jackrabbit/webdav/spi/nodetype/ server/src/java/org/apache/jackrabbit/webdav/spi/observation/ server/src/java/org/apache/jackrabbit/webdav/spi/property/ server/src/java/org/apache/jackrabbit/webdav/spi/search/ server/src/java/org/apache/jackrabbit/webdav/spi/transaction/ server/src/java/org/apache/jackrabbit/webdav/spi/version/ server/src/java/org/apache/jackrabbit/webdav/spi/version/report/ webapp/ webapp/src/webapp/WEB-INF/ webdav/ webdav/src/java/org/apache/jackrabbit/ webdav/src/java/org/apache/jackrabbit/webdav/ webdav/src/java/org/apache/jackrabbit/webdav/header/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/lock/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/version/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/version/report/ webdav/src/java/org/apache/jackrabbit/webdav/lock/ webdav/src/java/org/apache/jackrabbit/webdav/search/
Date Sun, 17 Apr 2005 14:52:14 GMT
Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/JcrConstants.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/JcrConstants.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatus.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatus.java?view=diff&r1=161659&r2=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatus.java (original)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatus.java Sun Apr 17 07:51:59 2005
@@ -21,14 +21,16 @@
 
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * MultiStatus representing the content of a multistatus response body and
  * allows to retrieve the Xml representation.
  */
-public class MultiStatus {
+public class MultiStatus implements DavConstants {
 
     private ArrayList responses = new ArrayList();
+    private String responseDescription;
 
     /**
      * Add response(s) to this multistatus, in order to build a multistatus for
@@ -64,7 +66,7 @@
      */
     public void addResourceProperties(DavResource resource, DavPropertyNameSet propNameSet,
 				      int depth) {
-	addResourceProperties(resource, propNameSet, DavConstants.PROPFIND_BY_PROPERTY, depth);
+	addResourceProperties(resource, propNameSet, PROPFIND_BY_PROPERTY, depth);
     }
 
     /**
@@ -95,16 +97,79 @@
     }
 
     /**
+     * Returns the multistatus responses present as array.
+     *
+     * @return array of all {@link MultiStatusResponse responses} present in this
+     * multistatus.
+     */
+    public MultiStatusResponse[] getResponses() {
+	return (MultiStatusResponse[]) responses.toArray(new MultiStatusResponse[responses.size()]);
+    }
+
+    /**
+     * Set the response description.
+     *
+     * @param responseDescription
+     */
+    public void setResponseDescription(String responseDescription) {
+        this.responseDescription = responseDescription;
+    }
+
+    /**
+     * Returns the response description.
+     *
+     * @return responseDescription
+     */
+    public String getResponseDescription() {
+	return responseDescription;
+    }
+
+    /**
      * Return the Xml representation of this <code>MultiStatus</code>.
      *
      * @return Xml document
      */
     public Document toXml() {
-	Element multistatus = new Element(DavConstants.XML_MULTISTATUS, DavConstants.NAMESPACE);
+	Element multistatus = new Element(XML_MULTISTATUS, NAMESPACE);
         Iterator it = responses.iterator();
 	while(it.hasNext()) {
 	    multistatus.addContent(((MultiStatusResponse)it.next()).toXml());
 	}
+        if (responseDescription != null) {
+            multistatus.addContent(new Element(XML_RESPONSEDESCRIPTION, NAMESPACE).setText(responseDescription));
+        }
 	return new Document(multistatus);
+    }
+
+    /**
+     * Build a <code>MultiStatus</code> from the specified xml document.
+     *
+     * @param multistatusDocument
+     * @return new <code>MultiStatus</code> instance.
+     * @throws IllegalArgumentException if the given document is <code>null</code>
+     * or does not provide the required element.
+     */
+    public static MultiStatus createFromXml(Document multistatusDocument) {
+        if (multistatusDocument == null) {
+	    throw new IllegalArgumentException("Cannot create a MultiStatus object from a null xml document.");
+	}
+
+	Element msElem = multistatusDocument.getRootElement();
+	if (!(XML_MULTISTATUS.equals(msElem.getName()) && NAMESPACE.equals(msElem.getNamespace()))) {
+	    throw new IllegalArgumentException("DAV:multistatus element expected.");
+	}
+
+        MultiStatus multistatus = new MultiStatus();
+
+	List respList = msElem.getChildren(XML_RESPONSE, NAMESPACE);
+	Iterator it = respList.iterator();
+	while (it.hasNext()) {
+            MultiStatusResponse response = MultiStatusResponse.createFromXml((Element)it.next());
+            multistatus.addResponse(response);
+	}
+
+	// optional response description on the multistatus element
+	multistatus.setResponseDescription(msElem.getChildText(XML_RESPONSEDESCRIPTION, NAMESPACE));
+        return multistatus;
     }
 }

Modified: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatusResponse.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatusResponse.java?view=diff&r1=161659&r2=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatusResponse.java (original)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/MultiStatusResponse.java Sun Apr 17 07:51:59 2005
@@ -18,6 +18,7 @@
 import org.jdom.Element;
 import org.apache.jackrabbit.webdav.property.*;
 import org.apache.jackrabbit.webdav.util.XmlUtil;
+import org.apache.log4j.Logger;
 
 import java.util.HashMap;
 import java.util.Iterator;
@@ -27,8 +28,11 @@
  * hold a WebDAV multistatus response. Properties can be added to this response
  * with the respective error/status code.
  */
+// todo: the propstat element may also contain a responsedescription (currently ignored)
 public class MultiStatusResponse implements DavConstants {
 
+    private static Logger log = Logger.getLogger(MultiStatusResponse.class);
+
     /**
      * The content the 'href' element for this response
      */
@@ -133,57 +137,117 @@
     }
 
     /**
+     * Returns the href
+     *
+     * @return href
+     */
+    public String getHref() {
+        return href;
+    }
+
+    /**
      * Adds a JDOM element to this response
      *
-     * @param prop the property to add
+     * @param propertyElem the property to add
      * @param status the status of the response set to select
      */
-    private void add(Element prop, int status) {
+    private void add(Element propertyElem, int status) {
         Integer statusKey = new Integer(status);
         Element propsContainer = (Element) statusMap.get(statusKey);
         if (propsContainer == null) {
             propsContainer = new Element(XML_PROP, NAMESPACE);
             statusMap.put(statusKey, propsContainer);
         }
-        propsContainer.addContent(prop);
+        propsContainer.addContent(propertyElem.detach());
     }
 
     /**
      * Adds a property to this response '200' propstat set.
      *
-     * @param prop the property to add
+     * @param property the property to add
      */
-    public void add(DavProperty prop) {
-        status200.addContent(prop.toXml());
+    public void add(DavProperty property) {
+        status200.addContent(property.toXml());
     }
 
     /**
      * Adds a property name to this response '200' propstat set.
      *
-     * @param name the property name to add
+     * @param propertyName the property name to add
      */
-    public void add(DavPropertyName name) {
-        status200.addContent(name.toXml());
+    public void add(DavPropertyName propertyName) {
+        status200.addContent(propertyName.toXml());
     }
 
     /**
      * Adds a property to this response
      *
-     * @param prop the property to add
+     * @param property the property to add
      * @param status the status of the response set to select
      */
-    public void add(DavProperty prop, int status) {
-        add(prop.toXml(), status);
+    public void add(DavProperty property, int status) {
+        add(property.toXml(), status);
     }
 
     /**
      * Adds a property name to this response
      *
-     * @param name the property name to add
+     * @param propertyName the property name to add
      * @param status the status of the response set to select
      */
-    public void add(DavPropertyName name, int status) {
-        add(name.toXml(), status);
+    public void add(DavPropertyName propertyName, int status) {
+        add(propertyName.toXml(), status);
+    }
+
+    /**
+     * Get properties present in this response for the given status code.
+     * @param status
+     * @return property set
+     */
+    public DavPropertySet getProperties(int status) {
+        DavPropertySet set = new DavPropertySet();
+        Integer key = new Integer(status);
+        if (statusMap.containsKey(key)) {
+            Element propElem = (Element) statusMap.get(key);
+            if (propElem != null) {
+                Iterator it = propElem.getChildren().iterator();
+                while (it.hasNext()) {
+                    Element propEntry = (Element) it.next();
+                    DavProperty prop = DefaultDavProperty.createFromXml(propEntry);
+                    set.add(prop);
+                }
+            }
+        }
+        return set;
+    }
+
+    /**
+     * Get property names present in this response for the given status code.
+     *
+     * @param status
+     * @return property names
+     */
+    public DavPropertyNameSet getPropertyNames(int status) {
+        DavPropertyNameSet set = new DavPropertyNameSet();
+        Integer key = new Integer(status);
+        if (statusMap.containsKey(key)) {
+            Element propElem = (Element) statusMap.get(key);
+            if (propElem != null) {
+                Iterator it = propElem.getChildren().iterator();
+                while (it.hasNext()) {
+                    Element propEntry = (Element) it.next();
+                    set.add(DavPropertyName.createFromXml(propEntry));
+                }
+            }
+        }
+        return set;
+    }
+
+    /**
+     * @return responseDescription
+     */
+    public String getResponseDescription() {
+	return responseDescription;
     }
 
     /**
@@ -207,31 +271,26 @@
         if ("".equals(href)) {
             return null;
         }
-
         Element response= new Element(XML_RESPONSE, NAMESPACE);
-
         // add '<href>'
         response.addContent(XmlUtil.hrefToXml(href));
-
         // add '<propstat>' elements or a single '<status>' element
         Iterator iter = statusMap.keySet().iterator();
         while (iter.hasNext()) {
             Integer statusKey = (Integer) iter.next();
 	    Element prop = (Element) statusMap.get(statusKey);
             if (prop != null) {
-                Element status = new Element(XML_STATUS, NAMESPACE);
-                status.setText("HTTP/1.1 " + statusKey + " " + DavException.getStatusPhrase(statusKey.intValue()));
-
+                Status status = new Status(statusKey.intValue());
                 if (XML_PROP.equals(prop.getName())) {
                     // do not add empty propstat elements
                     if (prop.getContentSize() > 0) {
                         Element propstat = new Element(XML_PROPSTAT, NAMESPACE);
                         propstat.addContent(prop);
-                        propstat.addContent(status);
+                        propstat.addContent(status.toXml());
                         response.addContent(propstat);
                     }
                 } else {
-                    response.addContent(status);
+                    response.addContent(status.toXml());
                 }
 	    }
         }
@@ -242,5 +301,125 @@
             response.addContent(desc);
         }
         return response;
+    }
+
+    /**
+     * Build a new response object from the given xml element.
+     *
+     * @param responseElement
+     * @return new <code>MultiStatusResponse</code> instance
+     * @throws  IllegalArgumentException if the specified element is <code>null</code>
+     */
+    public static MultiStatusResponse createFromXml(Element responseElement) {
+        if (responseElement == null) {
+	    throw new IllegalArgumentException("The response element must not be null.");
+	}
+        String href = responseElement.getChildText(XML_HREF, NAMESPACE);
+        String statusLine = responseElement.getChildText(XML_STATUS, NAMESPACE);
+        MultiStatusResponse response = (statusLine != null) ? new MultiStatusResponse(href, Status.createFromStatusLine(statusLine).getStatusCode()) : new MultiStatusResponse(href);
+
+        // read propstat elements
+        Iterator it = responseElement.getChildren(XML_PROPSTAT, NAMESPACE).iterator();
+	while (it.hasNext()) {
+	    Element propstat = (Element)it.next();
+            Element prop = propstat.getChild(XML_PROP, NAMESPACE);
+            String propstatus = propstat.getChildText(XML_STATUS, NAMESPACE);
+            if (propstatus != null) {
+                Status st = Status.createFromStatusLine(propstatus);
+                Element[] propertyElems = (Element[]) prop.getChildren().toArray(new Element[0]);
+                for (int i = 0; i < propertyElems.length; i++) {
+                    response.add(propertyElems[i], st.getStatusCode());
+                }
+            }
+            // todo: propstat may also contain a responsedescription
+	}
+
+        response.setResponseDescription(responseElement.getChildText(XML_RESPONSEDESCRIPTION, NAMESPACE));
+        return response;
+    }
+
+    /**
+     * Inner class encapsulating the 'status' present in the multistatus response.
+     */
+    private static class Status {
+
+        private String version = "HTTP/1.1";
+        private int code;
+        private String phrase = "";
+
+        private Status(int code) {
+            this.code = code;
+            phrase = DavException.getStatusPhrase(code);
+        }
+
+        private Status(String version, int code, String phrase) {
+            this.version = version;
+            this.code = code;
+            this.phrase = phrase;
+        }
+
+        private int getStatusCode() {
+            return code;
+        }
+
+        private Element toXml() {
+            String statusLine = version + " " + code + " " + phrase;
+            return new Element(XML_STATUS, NAMESPACE).setText(statusLine);
+        }
+
+        private static Status createFromStatusLine(String statusLine) {
+            if (statusLine == null) {
+                throw new IllegalArgumentException("Unable to parse status line from null xml element.");
+            }
+            Status status;
+
+            // code copied from org.apache.commons.httpclient.StatusLine
+            int length = statusLine.length();
+            int at = 0;
+            int start = 0;
+            try {
+                while (Character.isWhitespace(statusLine.charAt(at))) {
+                    ++at;
+                    ++start;
+                }
+                if (!"HTTP".equals(statusLine.substring(at, at += 4))) {
+                    log.warn("Status-Line '" + statusLine + "' does not start with HTTP");
+                }
+                //handle the HTTP-Version
+                at = statusLine.indexOf(" ", at);
+                if (at <= 0) {
+                    log.warn("Unable to parse HTTP-Version from the status line: '"+ statusLine + "'");
+                }
+                String version = (statusLine.substring(start, at)).toUpperCase();
+                //advance through spaces
+                while (statusLine.charAt(at) == ' ') {
+                    at++;
+                }
+                //handle the Status-Code
+                int to = statusLine.indexOf(" ", at);
+                if (to < 0) {
+                    to = length;
+                }
+                try {
+                    int code = Integer.parseInt(statusLine.substring(at, to));
+                    status = new Status(code);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Unable to parse status code from status line: '"+ statusLine + "'");
+                }
+                //handle the Reason-Phrase
+                String phrase = "";
+                at = to + 1;
+                if (at < length) {
+                    phrase = statusLine.substring(at).trim();
+                }
+
+                status.version = version;
+                status.phrase = phrase;
+
+            } catch (StringIndexOutOfBoundsException e) {
+                throw new IllegalArgumentException("Status-Line '" + statusLine + "' is not valid");
+            }
+            return status;
+        }
     }
 }

Modified: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java?view=diff&r1=161659&r2=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java (original)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java Sun Apr 17 07:51:59 2005
@@ -27,6 +27,8 @@
 import org.apache.jackrabbit.webdav.version.report.ReportInfo;
 import org.apache.jackrabbit.webdav.ordering.*;
 import org.apache.jackrabbit.webdav.header.DepthHeader;
+import org.apache.jackrabbit.webdav.header.IfHeader;
+import org.apache.jackrabbit.webdav.header.CodedUrlHeader;
 import org.jdom.input.SAXBuilder;
 import org.jdom.JDOMException;
 import org.jdom.Document;
@@ -180,7 +182,7 @@
      * @see DavServletRequest#getDepth(int)
      */
     public int getDepth(int defaultValue) {
-	return DepthHeader.parse(httpRequest.getHeader(HEADER_DEPTH), defaultValue).getDepth();
+	return DepthHeader.parse(httpRequest, defaultValue).getDepth();
     }
 
     /**
@@ -233,7 +235,7 @@
      * @see DavServletRequest#getLockToken()
      */
     public String getLockToken() {
-	return getCodedURLHeader(HEADER_LOCK_TOKEN);
+        return CodedUrlHeader.parse(httpRequest, HEADER_LOCK_TOKEN).getCodedUrl();
     }
 
     /**
@@ -480,45 +482,16 @@
     public boolean matchesIfHeader(String href, String token, String eTag) {
         return ifHeader.matches(href, token, eTag);
     }
-
-    /**
-     * Retrieve the header with the given header name and parse the CodedURL
-     * value included.
-     *
-     * @param headerName
-     * @return token present in the CodedURL header or <code>null</code> if
-     * the header is not present.
-     */
-    private String getCodedURLHeader(String headerName) {
-        String headerValue = null;
-	String header = httpRequest.getHeader(headerName);
-	if (header != null) {
-	    int p1 = header.indexOf('<');
-	    if (p1<0) {
-		throw new IllegalArgumentException("Invalid CodedURL header value:"+header);
-	    }
-	    int p2 = header.indexOf('>', p1);
-	    if (p2<0) {
-		throw new IllegalArgumentException("Invalid CodedURL header value:"+header);
-	    }
-	    headerValue = header.substring(p1+1, p2);
-	}
-	return headerValue;
-    }
     
     //-----------------------------< TransactionDavServletRequest Interface >---
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.transaction.TransactionDavServletRequest#getTransactionId()
      */
     public String getTransactionId() {
-        return getCodedURLHeader(TransactionConstants.HEADER_TRANSACTIONID);
+        return CodedUrlHeader.parse(httpRequest, TransactionConstants.HEADER_TRANSACTIONID).getCodedUrl();
     }
 
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.transaction.TransactionDavServletRequest#getTransactionInfo()
      */
     public TransactionInfo getTransactionInfo() {
@@ -535,17 +508,13 @@
 
     //-----------------------------< ObservationDavServletRequest Interface >---
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.observation.ObservationDavServletRequest#getSubscriptionId()
      */
     public String getSubscriptionId() {
-        return getCodedURLHeader(ObservationConstants.HEADER_SUBSCRIPTIONID);
+        return CodedUrlHeader.parse(httpRequest, ObservationConstants.HEADER_SUBSCRIPTIONID).getCodedUrl();
     }
 
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.observation.ObservationDavServletRequest#getSubscriptionInfo()
      */
     public SubscriptionInfo getSubscriptionInfo() {
@@ -562,8 +531,6 @@
 
     //--------------------------------< OrderingDavServletRequest Interface >---
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.ordering.OrderingDavServletRequest#getOrderingType()
      */
     public String getOrderingType() {
@@ -571,8 +538,6 @@
     }
 
     /**
-     *
-     * @return
      * @see org.apache.jackrabbit.webdav.ordering.OrderingDavServletRequest#getPosition()
      */
     public Position getPosition() {
@@ -592,7 +557,6 @@
     }
 
     /**
-     *
      * @return <code>OrderPatch</code> object representing the orderpatch request
      * body or <code>null</code> if the
      * @see org.apache.jackrabbit.webdav.ordering.OrderingDavServletRequest#getOrderPatch()

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2004 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.jackrabbit.webdav.header;
+
+import org.apache.log4j.Logger;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * <code>CodedUrlHeader</code>...
+ */
+public class CodedUrlHeader {
+
+    private static Logger log = Logger.getLogger(CodedUrlHeader.class);
+
+    private final String headerName;
+    private final String headerValue;
+
+    public CodedUrlHeader(String headerName, String headerValue) {
+        this.headerName = headerName;
+        if (headerValue != null && !(headerValue.startsWith("<") && headerValue.endsWith(">"))) {
+            headerValue = "<" + headerValue + ">";
+        }
+        this.headerValue = headerValue;
+    }
+
+    /**
+     * Return the name of the header
+     *
+     * @return header name
+     */
+    public String getHeaderName() {
+	return headerName;
+    }
+
+    /**
+     * Return the value of the header
+     *
+     * @return value
+     */
+    public String getHeaderValue() {
+        return headerValue;
+    }
+
+    /**
+     * Returns the token present in the header value or <code>null</code>.
+     * If the header contained multiple tokens separated by ',' the first value
+     * is returned.
+     *
+     * @return token present in the CodedURL header or <code>null</code> if
+     * the header is not present.
+     * @see #getCodedUrls()
+     */
+    public String getCodedUrl() {
+        String[] codedUrls = getCodedUrls();
+        return (codedUrls != null) ? codedUrls[0] : null;
+    }
+    
+    /**
+     * Return an array of coded urls as present in the header value or <code>null</code> if
+     * no value is present.
+     *
+     * @return array of coded urls
+     */
+    public String[] getCodedUrls() {
+        String[] codedUrls = null;
+        if (headerValue != null) {
+            String[] values = headerValue.split(",");
+            codedUrls = new String[values.length];
+            for (int i = 0; i < values.length; i++) {
+                int p1 = values[i].indexOf('<');
+                if (p1<0) {
+                    throw new IllegalArgumentException("Invalid CodedURL header value:" + values[i]);
+                }
+                int p2 = values[i].indexOf('>', p1);
+                if (p2<0) {
+                    throw new IllegalArgumentException("Invalid CodedURL header value:" + values[i]);
+                }
+                codedUrls[i] = values[i].substring(p1+1, p2);
+            }
+	}
+        return codedUrls;
+    }
+
+    /**
+     * Retrieves the header with the given name and builds a new <code>CodedUrlHeader</code>.
+     *
+     * @param request
+     * @param headerName
+     * @return new <code>CodedUrlHeader</code> instance
+     */
+    public static CodedUrlHeader parse(HttpServletRequest request, String headerName) {
+        String headerValue = request.getHeader(headerName);
+        return new CodedUrlHeader(headerName, headerValue);
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/CodedUrlHeader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/DepthHeader.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/DepthHeader.java?view=diff&r1=161659&r2=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/DepthHeader.java (original)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/DepthHeader.java Sun Apr 17 07:51:59 2005
@@ -18,6 +18,8 @@
 import org.apache.log4j.Logger;
 import org.apache.jackrabbit.webdav.DavConstants;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * <code>DepthHeader</code>...
  */
@@ -64,21 +66,23 @@
      */
     public String getHeaderValue() {
         if (depth == DavConstants.DEPTH_0 || depth == DavConstants.DEPTH_1) {
-	    return depth + "";
+	    return String.valueOf(depth);
 	} else {
 	    return DavConstants.DEPTH_INFINITY_S;
 	}
     }
 
     /**
-     * Parse the given header value or use the defaultValue if the header
-     * string is empty or <code>null</code>.
+     * Retrieve the Depth header from the given request object and parse the
+     * value. If no header is present or the value is empty String, the
+     * defaultValue is used ot build a new <code>DepthHeader</code> instance.
      *
-     * @param headerValue
+     * @param request
      * @param defaultValue
-     * @return a new DepthHeader
+     * @return a new <code>DepthHeader</code> instance
      */
-    public static DepthHeader parse(String headerValue, int defaultValue) {
+    public static DepthHeader parse(HttpServletRequest request, int defaultValue) {
+        String headerValue = request.getHeader(HEADER_DEPTH);
         if (headerValue == null || "".equals(headerValue)) {
 	    return new DepthHeader(defaultValue);
 	} else {

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,844 @@
+/*
+ * Copyright 2005 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.jackrabbit.webdav.header;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.DavConstants;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.io.StringReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * The <code>IfHeader</code> class represents the state lists defined
+ * through the HTTP <em>If</em> header, which is specified in RFC 2518 as
+ * follows :
+ * <pre>
+     If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
+     No-tag-list = List
+     Tagged-list = Resource 1*List
+     Resource = Coded-URL
+     List = "(" 1*(["Not"](State-etag | "[" entity-tag "]")) ")"
+     State-etag = Coded-URL
+     Coded-URL = "<" absoluteURI ">"
+ * </pre>
+ * <p>
+ * Reformulating this specification into proper EBNF as specified by N. Wirth
+ * we get the following productions, which map to the parse METHODS of this
+ * class. Any whitespace is ignored except for white space surrounding and
+ * within words which is considered significant.
+ * <pre>
+     If = "If:" ( Tagged | Untagged ).
+     Tagged = { "<" Word ">" Untagged } .
+     Untagged = { "(" IfList ")" } .
+     IfList = { [ "Not" ] ( ("<" Word ">" ) | ( "[" Word "]" ) ) } .
+     Word = characters .
+ * </pre>
+ * <p>
+ * An <em>If</em> header either contains untagged <em>IfList</em> entries or
+ * tagged <em>IfList</em> entries but not a mixture of both. An <em>If</em>
+ * header containing tagged entries is said to be of <em>tagged</em> type while
+ * an <em>If</em> header containing untagged entries is said to be of
+ * <em>untagged</em> type.
+ * <p>
+ * An <em>IfList</em> is a list of tokens - words enclosed in <em>&lt; &gt;</em>
+ * - and etags - words enclosed in <em>[ ]</em>. An <em>IfList</em> matches a
+ * (token, etag) tuple if all entries in the list match. If an entry in the list
+ * is prefixed with the word <em>Not</em> (parsed case insensitively) the entry
+ * must not match the concrete token or etag.
+ * <p>
+ * Example: The <em>ifList</em> <code>(&lt;token&gt; [etag])</code> only matches
+ * if the concret token has the value <code>token</code> and the conrete etag
+ * has the value <code>etag</code>. On the other hand, the <em>ifList</em>
+ * <code>(Not &lt;notoken&gt;)</code> matches any token which is not
+ * <code>notoken</code> (in this case the concrete value of the etag is
+ * not taken into consideration).
+ *
+ * @author Felix Meschberger
+ */
+public class IfHeader {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = Logger.getLogger(IfHeader.class);
+
+    /**
+     * The list of untagged state entries
+     */
+    private final IfHeaderInterface ifHeader;
+
+    /**
+     * The list of all tokens present in the If header.
+     */
+    private List allTokens = new ArrayList();
+
+    /**
+     * Parses the <em>If</em> header and creates and internal representation
+     * which is easy to query.
+     *
+     * @param req The request object
+     */
+    public IfHeader(HttpServletRequest req) {
+
+	String ifHeaderValue = req.getHeader(DavConstants.HEADER_IF);
+        if (ifHeaderValue != null && ifHeaderValue.length() > 0) {
+
+            StringReader reader = null;
+            int firstChar = 0;
+
+            try {
+                reader = new StringReader(ifHeaderValue);
+
+                // get the first character to decide - expect '(' or '<'
+                try {
+                    reader.mark(1);
+                    firstChar = readWhiteSpace(reader);
+                    reader.reset();
+                } catch (IOException ignore) {
+                    // may be thrown according to API but is only thrown by the
+                    // StringReader class if the reader is already closed.
+                }
+
+                if (firstChar == '(') {
+                    ifHeader = parseUntagged(reader);
+                } else if (firstChar == '<') {
+                    ifHeader = parseTagged(reader);
+                } else {
+                    logIllegalState("If", firstChar, "(<", null);
+                    ifHeader = null;
+                }
+
+            } finally  {
+                if (reader != null) {
+                    reader.close();
+                }
+            }
+
+        } else {
+            log.debug("IfHeader: No If header in request");
+            ifHeader = null;
+        }
+    }
+
+    /**
+     * Return {@link DavConstants#HEADER_IF If}
+     *
+     * @return {@link DavConstants#HEADER_IF If}
+     * @see DavConstants#HEADER_IF
+     */
+    public String getHeaderName() {
+	return DavConstants.HEADER_IF;
+    }
+
+    /**
+     * Tries to match the contents of the <em>If</em> header with the given
+     * token and etag values with the restriction to only check for the tag.
+     * <p>
+     * If the <em>If</em> header is of untagged type, the untagged <em>IfList</em>
+     * is matched against the token and etag given: A match of the token and
+     * etag is found if at least one of the <em>IfList</em> entries match the
+     * token and etag tupel.
+     *
+     * @param tag The tag to identify the <em>IfList</em> to match the token
+     * and etag against.
+     * @param token The token to compare.
+     * @param etag The ETag value to compare.
+     *
+     * @return If the <em>If</em> header is of untagged type the result is
+     *      <code>true</code> if any of the <em>IfList</em> entries matches
+     *      the token and etag values. For tagged type <em>If</em> header the
+     *      result is <code>true</code> if either no entry for the given tag
+     *      exists in the <em>If</em> header or if the <em>IfList</em> for the
+     *      given tag matches the token and etag given.
+     */
+    public boolean matches(String tag, String token, String etag) {
+        if (ifHeader == null) {
+            log.debug("matches: No If header, assume match");
+            return true;
+        } else {
+            return ifHeader.matches(tag, token, etag);
+        }
+    }
+
+    /**
+     * 
+     * @return
+     */
+    public Iterator getAllTokens() {
+        return allTokens.iterator();
+    }
+
+    //---------- internal IF header parser -------------------------------------
+    /**
+     * Parses a tagged type <em>If</em> header. This method implements the
+     * <em>Tagged</em> production given in the class comment :
+     * <pre>
+         Tagged = { "<" Word ">" Untagged } .
+     * </pre>
+     *
+     * @param reader
+     * @return
+     */
+    private IfHeaderMap parseTagged(StringReader reader) {
+        IfHeaderMap map = new IfHeaderMap();
+        try {
+            while (true) {
+                // read next non-whitespace
+                int c = readWhiteSpace(reader);
+                if (c < 0) {
+                    // end of input, no more entries
+                    break;
+                } else if (c == '<') {
+                    // start a tag with an IfList
+                    String resource = readWord(reader, '>');
+                    if (resource != null) {
+                        // go to untagged after reading the resource
+                        map.put(resource, parseUntagged(reader));
+                    } else {
+                        break;
+                    }
+                } else {
+                    // unexpected character
+                    // catchup to end of input or start of a tag
+                    logIllegalState("Tagged", c, "<", reader);
+                }
+            }
+        } catch (IOException ioe) {
+            log.error("parseTagged: Problem parsing If header: "+ioe.toString());
+        }
+
+        return map;
+    }
+
+    /**
+     * Parses an untagged type <em>If</em> header. This method implements the
+     * <em>Untagged</em> production given in the class comment :
+     * <pre>
+         Untagged = { "(" IfList ")" } .
+     * </pre>
+     *
+     * @param reader The <code>StringReader</code> to read from for parsing
+     *
+     * @return An <code>ArrayList</code> of {@link IfList} entries.
+     */
+    private IfHeaderList parseUntagged(StringReader reader) {
+        IfHeaderList list = new IfHeaderList();
+        try {
+            while (true) {
+                // read next non-whitespace
+                reader.mark(1);
+                int c = readWhiteSpace(reader);
+                if (c < 0) {
+                    // end of input, no more IfLists
+                    break;
+
+                } else if (c == '(') {
+                    // start of an IfList, parse
+                    list.add(parseIfList(reader));
+
+                } else if (c == '<') {
+                    // start of a tag, return current list
+                    reader.reset();
+                    break;
+
+                } else {
+                    // unexpected character
+                    // catchup to end of input or start of an IfList
+                    logIllegalState("Untagged", c, "(", reader);
+                }
+            }
+        } catch (IOException ioe) {
+            log.error("parseUntagged: Problem parsing If header: "+ioe.toString());
+        }
+        return list;
+    }
+
+    /**
+     * Parses an <em>IfList</em> in the <em>If</em> header. This method
+     * implements the <em>Tagged</em> production given in the class comment :
+     * <pre>
+         IfList = { [ "Not" ] ( ("<" Word ">" ) | ( "[" Word "]" ) ) } .
+     * </pre>
+     *
+     * @param reader The <code>StringReader</code> to read from for parsing
+     *
+     * @return The {@link IfList} for the input <em>IfList</em>.
+     *
+     * @throws IOException if a problem occurrs during reading.
+     */
+    private IfList parseIfList(StringReader reader) throws IOException {
+        IfList res = new IfList();
+        boolean positive = true;
+        String word;
+
+        ReadLoop:
+        while (true) {
+            int nextChar = readWhiteSpace(reader);
+            switch (nextChar) {
+                case 'N':
+                case 'n':
+                    // read not
+
+                    // check whether o or O
+                    int not = reader.read();
+                    if (not != 'o' && not != 'O') {
+                        logIllegalState("IfList-Not", not, "o", null);
+                        break;
+                    }
+
+                    // check whether t or T
+                    not = reader.read();
+                    if (not !='t' || not != 'T') {
+                        logIllegalState("IfList-Not", not, "t", null);
+                        break;
+                    }
+
+                    // read Not ok
+                    positive = false;
+                    break;
+
+                case '<':
+                    // state token
+                    word = readWord(reader, '>');
+                    if (word != null) {
+                        res.add(new IfListEntryToken(word, positive));
+                        // also add the token to the list of all tokens
+                        allTokens.add(word);
+                        positive = true;
+                    }
+                    break;
+
+                case '[':
+                    // etag
+                    word = readWord(reader, ']');
+                    if (word != null) {
+                        res.add(new IfListEntryEtag(word, positive));
+                        positive = true;
+                    }
+                    break;
+
+                case ')':
+                    // correct end of list, end the loop
+                    log.debug("parseIfList: End of If list, terminating loop");
+                    break ReadLoop;
+
+                default:
+                    logIllegalState("IfList", nextChar, "nN<[)", reader);
+
+                    // abort loop if EOF
+                    if (nextChar < 0) {
+                        break ReadLoop;
+                    }
+
+                    break;
+            }
+        }
+
+        // return the current list anyway
+        return res;
+    }
+
+    /**
+     * Returns the first non-whitespace character from the reader or -1 if
+     * the end of the reader is encountered.
+     *
+     * @param reader The <code>Reader</code> to read from
+     *
+     * @return The first non-whitespace character or -1 in case of EOF.
+     *
+     * @throws IOException if a problem occurrs during reading.
+     */
+    private int readWhiteSpace(Reader reader) throws IOException {
+        int c = reader.read();
+        while (c >= 0 && Character.isWhitespace((char) c)) {
+             c = reader.read();
+        }
+        return c;
+    }
+
+    /**
+     * Reads from the input until the end character is encountered and returns
+     * the string upto but not including this end character. If the end of input
+     * is reached before reading the end character <code>null</code> is
+     * returned.
+     * <p>
+     * Note that this method does not support any escaping.
+     *
+     * @param reader The <code>Reader</code> to read from
+     * @param end The ending character limitting the word.
+     *
+     * @return The string read upto but not including the ending character or
+     *      <code>null</code> if the end of input is reached before the ending
+     *      character has been read.
+     *
+     * @throws IOException if a problem occurrs during reading.
+     */
+    private String readWord(Reader reader, char end) throws IOException {
+        StringBuffer buf = new StringBuffer();
+
+        // read the word value
+        int c = reader.read();
+        for (; c >= 0 && c != end; c=reader.read()) {
+            buf.append((char) c);
+        }
+
+        // check whether we succeeded
+        if (c < 0) {
+            log.error("readWord: Unexpected end of input reading word");
+            return null;
+        }
+
+        // build the string and return it
+        return buf.toString();
+    }
+
+    /**
+     * Logs an unexpected character with the corresponding state and list of
+     * expected characters. If the reader parameter is not null, characters
+     * are read until either the end of the input is reached or any of the
+     * characters in the expChar string is read.
+     *
+     * @param state The name of the current parse state. This method logs this
+     *      name in the message. The intended value would probably be the
+     *      name of the EBNF production during which the error occurrs.
+     * @param effChar The effective character read.
+     * @param expChar The list of characters acceptable in the current state.
+     * @param reader The reader to be caught up to any of the expected
+     *      characters. If <code>null</code> the input is not caught up to
+     *      any of the expected characters (of course ;-).
+     */
+    private void logIllegalState(String state, int effChar, String expChar,
+                                 StringReader reader) {
+
+        // format the effective character to be logged
+        String effString = (effChar < 0) ? "<EOF>" : String.valueOf((char) effChar);
+
+        // log the error
+        log.error("logIllegalState: Unexpected character '"+effString+" in state "+state+", expected any of "+expChar);
+
+        // catch up if a reader is given
+        if (reader != null && effChar >= 0) {
+            try {
+                log.debug("logIllegalState: Catch up to any of "+expChar);
+                do {
+                    reader.mark(1);
+                    effChar = reader.read();
+                } while (effChar >= 0 && expChar.indexOf(effChar) < 0);
+                if (effChar >= 0) {
+                    reader.reset();
+                }
+            } catch (IOException ioe) {
+                log.error("logIllegalState: IO Problem catching up to any of "+expChar);
+            }
+        }
+    }
+
+    //---------- internal If header structure ----------------------------------
+
+    /**
+     * The <code>IfListEntry</code> abstract class is the base class for
+     * entries in an <em>IfList</em> production. This abstract base class
+     * provides common functionality to both types of entries, namely tokens
+     * enclosed in angle brackets (<code>&lt; &gt;</code>) and etags enclosed
+     * in square brackets (<code>[ ]</code>).
+     */
+    private static abstract class IfListEntry {
+
+        /**
+         * The entry string value - the semantics of this value depends on the
+         * implementing class.
+         */
+        protected final String value;
+
+        /** Flag to indicate, whether this is a positive match or not */
+        protected final boolean positive;
+
+        /** The cached result of the {@link #toString} method. */
+        protected String stringValue;
+
+        /**
+         * Sets up the final fields of this abstract class. The meaning of
+         * value parameter depends solely on the implementing class. From the
+         * point of view of this abstract class, it is simply a string value.
+         *
+         * @param value The string value of this instance
+         * @param positive <code>true</code> if matches are positive
+         */
+        protected IfListEntry(String value, boolean positive) {
+            this.value = value;
+            this.positive = positive;
+        }
+
+        /**
+         * Matches the value from the parameter to the internal string value.
+         * If the parameter and the {@link #value} field match, the method
+         * returns <code>true</code> for positive matches and <code>false</code>
+         * for negative matches.
+         * <p>
+         * This helper method can be called by implementations to evaluate the
+         * concrete match on the correct value parameter. See
+         * {@link #match(String, String)} for the external API method.
+         *
+         * @param value The string value to compare to the {@link #value}
+         *      field.
+         *
+         * @return <code>true</code> if the value parameter and the
+         *      {@link #value} field match and the {@link #positive} field is
+         *      <code>true</code> or if the values do not match and the
+         *      {@link #positive} field is <code>false</code>.
+         */
+        protected boolean match(String value) {
+            return positive == this.value.equals(value);
+        }
+
+        /**
+         * Matches the entry's value to the the token or etag. Depending on the
+         * concrete implementation, only one of the parameters may be evaluated
+         * while the other may be ignored.
+         * <p>
+         * Implementing METHODS may call the helper method {@link #match(String)}
+         * for the actual matching.
+         *
+         * @param token The token value to compare
+         * @param etag The etag value to compare
+         *
+         * @return <code>true</code> if the token/etag matches the <em>IfList</em>
+         *      entry.
+         */
+        public abstract boolean match(String token, String etag);
+
+        /**
+         * Returns a short type name for the implementation. This method is
+         * used by the {@link #toString} method to build the string representation
+         * if the instance.
+         *
+         * @return The type name of the implementation.
+         */
+        protected abstract String getType();
+
+	/**
+	 * Returns the value of this entry.
+	 *
+	 * @return the value
+	 */
+	protected String getValue() {
+	    return value;
+	}
+
+        /**
+         * Returns the String represenation of this entry. This method uses the
+         * {@link #getType} to build the string representation.
+         *
+         * @return the String represenation of this entry.
+         */
+        public String toString() {
+            if (stringValue == null) {
+                stringValue = getType() + ": " + (positive?"":"!") + value;
+            }
+            return stringValue;
+        }
+    }
+
+    /**
+     * The <code>IfListEntryToken</code> extends the {@link IfListEntry}
+     * abstract class to represent an entry for token matching.
+     */
+    private static class IfListEntryToken extends IfListEntry {
+
+        /**
+         * Creates a token matching entry.
+         *
+         * @param token The token value pertinent to this instance.
+         * @param positive <code>true</code> if this is a positive match entry.
+         */
+        IfListEntryToken(String token, boolean positive) {
+            super(token, positive);
+        }
+
+        /**
+         * Matches the token parameter to the stored token value and returns
+         * <code>true</code> if the values match and if the match is positive.
+         * <code>true</code> is also returned for negative matches if the values
+         * do not match.
+         *
+         * @param token The token value to compare
+         * @param etag The etag value to compare, which is ignored in this
+         *      implementation.
+         *
+         * @return <code>true</code> if the token matches the <em>IfList</em>
+         *      entry's token value.
+         */
+        public boolean match(String token, String etag) {
+            return super.match(token);
+        }
+
+        /**
+         * Returns the type name of this implementation, which is fixed to
+         * be <em>Token</em>.
+         *
+         * @return The fixed string <em>Token</em> as the type name.
+         */
+        protected String getType() {
+            return "Token";
+        }
+    }
+
+    /**
+     * The <code>IfListEntryToken</code> extends the {@link IfListEntry}
+     * abstract class to represent an entry for etag matching.
+     */
+    private static class IfListEntryEtag extends IfListEntry {
+
+        /**
+         * Creates an etag matching entry.
+         *
+         * @param etag The etag value pertinent to this instance.
+         * @param positive <code>true</code> if this is a positive match entry.
+         */
+        IfListEntryEtag(String etag, boolean positive) {
+            super(etag, positive);
+        }
+
+        /**
+         * Matches the etag parameter to the stored etag value and returns
+         * <code>true</code> if the values match and if the match is positive.
+         * <code>true</code> is also returned for negative matches if the values
+         * do not match.
+         *
+         * @param token The token value to compare, which is ignored in this
+         *      implementation.
+         * @param etag The etag value to compare
+         *
+         * @return <code>true</code> if the etag matches the <em>IfList</em>
+         *      entry's etag value.
+         */
+        public boolean match(String token, String etag) {
+            return super.match(etag);
+        }
+
+        /**
+         * Returns the type name of this implementation, which is fixed to
+         * be <em>ETag</em>.
+         *
+         * @return The fixed string <em>ETag</em> as the type name.
+         */
+        protected String getType() {
+            return "ETag";
+        }
+    }
+
+    /**
+     * The <code>IfList</code> class extends the <code>ArrayList</code> class
+     * with the limitation to only support adding {@link IfListEntry} objects
+     * and adding a {@link #match} method.
+     * <p>
+     * This class is a container for data contained in the <em>If</em>
+     * production <em>IfList</em>
+     * <pre>
+         IfList = { [ "Not" ] ( ("<" Word ">" ) | ( "[" Word "]" ) ) } .
+     * </pre>
+     * <p>
+     */
+    private static class IfList extends ArrayList {
+
+        /**
+         * Throws an <code>IllegalStateException</code> because only
+         * {@link IfListEntry} objects are supported in this list.
+         *
+         * @param o The <code>Object</code> to add.
+         * @return <code>true</code> if successfull
+         *
+         * @throws IllegalStateException because only {@link IfListEntry}
+         *      objects are supported in this list.
+         */
+        public boolean add(Object o) {
+            throw new IllegalArgumentException("Only IfListEntry instances allowed");
+        }
+
+        /**
+         * Throws an <code>IllegalStateException</code> because only
+         * {@link IfListEntry} objects are supported in this list.
+         *
+         * @param index The position at which to add the object.
+         * @param element The <code>Object</code> to add.
+         *
+         * @throws IllegalStateException because only {@link IfListEntry}
+         *      objects are supported in this list.
+         */
+        public void add(int index, Object element) {
+            throw new IllegalArgumentException("Only IfListEntry instances allowed");
+        }
+
+        /**
+         * Adds the {@link IfListEntry} at the end of the list.
+         *
+         * @param entry The {@link IfListEntry} to add to the list
+         *
+         * @return <code>true</code> (as per the general contract of
+         *      Collection.add).
+         */
+        public boolean add(IfListEntry entry) {
+            return super.add(entry);
+        }
+
+        /**
+         * Adds the {@link IfListEntry} at the indicated position of the list.
+         *
+         * @param index
+         * @param entry
+         *
+         * @throws IndexOutOfBoundsException if index is out of range
+         *      <code>(index &lt; 0 || index &gt; size())</code>.
+         */
+        public void add(int index, IfListEntry entry) {
+            super.add(index, entry);
+        }
+
+        /**
+         * Returns <code>true</code> if all {@link IfListEntry} objects in the
+         * list match the given token and etag. If the list is entry, it is
+         * considered to match the token and etag.
+         *
+         * @param token The token to compare.
+         * @param etag The etag to compare.
+         *
+         * @return <code>true</code> if all entries in the list matche the
+         *      given tag and token.
+         */
+        public boolean match(String token, String etag) {
+            log.debug("match: Trying to match token="+token+", etag="+etag);
+            for (int i=0; i < size(); i++) {
+                IfListEntry ile = (IfListEntry) get(i);
+                if (!ile.match(token, etag)) {
+                    log.debug("match: Entry "+String.valueOf(i)+"-"+ile+" does not match");
+                    return false;
+                }
+            }
+            // invariant: all entries matched
+
+            return true;
+        }
+    }
+
+    /**
+     * The <code>IfHeaderInterface</code> interface abstracts away the difference of
+     * tagged and untagged <em>If</em> header lists. The single method provided
+     * by this interface is to check whether a request may be applied to a
+     * resource with given token and etag.
+     */
+    private static interface IfHeaderInterface {
+
+        /**
+         * Matches the resource, token, and etag against this
+         * <code>IfHeaderInterface</code> instance.
+         *
+         * @param resource The resource to match this instance against. This
+         *      must be absolute URI of the resource as defined in Section 3
+         *      (URI Syntactic Components) of RFC 2396 Uniform Resource
+         *      Identifiers (URI): Generic Syntax.
+         * @param token The resource's lock token to match
+         * @param etag The resource's etag to match
+         *
+         * @return <code>true</code> if the header matches the resource with
+         *      token and etag, which means that the request is applicable
+         *      to the resource according to the <em>If</em> header.
+         */
+        public boolean matches(String resource, String token, String etag);
+    }
+
+    /**
+     * The <code>IfHeaderList</code> clss implements the {@link IfHeaderInterface}
+     * interface to support untagged lists of {@link IfList}s. This class
+     * implements the data container for the production :
+     * <pre>
+         Untagged = { "(" IfList ")" } .
+     * </pre>
+     */
+    private static class IfHeaderList extends ArrayList implements IfHeaderInterface {
+
+        /**
+         * Matches a list of {@link IfList}s against the token and etag. If any of
+         * the {@link IfList}s matches, the method returns <code>true</code>.
+         * On the other hand <code>false</code> is only returned if non of the
+         * {@link IfList}s match.
+         *
+         * @param resource The resource to match, which is ignored by this
+         *      implementation. A value of <code>null</code> is therefor
+         *      acceptable.
+         * @param token The token to compare.
+         * @param etag The ETag value to compare.
+         *
+         * @return <code>True</code> if any of the {@link IfList}s matches the token
+         *      and etag, else <code>false</code> is returned.
+         */
+        public boolean matches(String resource, String token, String etag) {
+            log.debug("matches: Trying to match token="+token+", etag="+etag);
+
+            for (int i=0; i < size(); i++) {
+                IfList il = (IfList) get(i);
+                if (il.match(token, etag)) {
+                    log.debug("matches: Found match with "+il);
+                    return true;
+                }
+            }
+            // invariant: no match found
+
+            return false;
+        }
+    }
+
+    /**
+     * The <code>IfHeaderMap</code> clss implements the {@link IfHeaderInterface}
+     * interface to support tagged lists of {@link IfList}s. This class
+     * implements the data container for the production :
+     * <pre>
+         Tagged = { "<" Word ">" "(" IfList ")" } .
+     * </pre>
+     */
+    private static class IfHeaderMap extends HashMap implements IfHeaderInterface {
+
+        /**
+         * Matches the token and etag for the given resource. If the resource is
+         * not mentioned in the header, a match is assumed and <code>true</code>
+         * is returned in this case.
+         *
+         * @param resource The absolute URI of the resource for which to find
+         *      a match.
+         * @param token The token to compare.
+         * @param etag The etag to compare.
+         *
+         * @return <code>true</code> if either no entry exists for the resource
+         *      or if the entry for the resource matches the token and etag.
+         */
+        public boolean matches(String resource, String token, String etag) {
+            log.debug("matches: Trying to match resource="+resource+", token="+token+","+etag);
+
+            IfHeaderList list = (IfHeaderList) get(resource);
+            if (list == null) {
+                log.debug("matches: No entry for tag "+resource+", assuming match");
+                return true;
+            } else {
+                return list.matches(resource, token, etag);
+            }
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/IfHeader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2005 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.jackrabbit.webdav.jcr;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.property.*;
+import org.apache.jackrabbit.webdav.*;
+import org.apache.jackrabbit.webdav.jcr.version.report.NodeTypesReport;
+import org.apache.jackrabbit.webdav.jcr.version.report.ExportViewReport;
+import org.apache.jackrabbit.webdav.jcr.version.report.LocateByUuidReport;
+import org.apache.jackrabbit.webdav.jcr.version.report.RegisteredNamespacesReport;
+import org.apache.jackrabbit.webdav.transaction.TxLockEntry;
+import org.apache.jackrabbit.webdav.version.report.SupportedReportSetProperty;
+import org.apache.jackrabbit.webdav.version.report.ReportType;
+import org.apache.jackrabbit.webdav.util.Text;
+
+import javax.jcr.*;
+
+/**
+ * <code>AbstractItemResource</code> covers common functionality for the various
+ * resources, that represent a repository item.
+ */
+abstract class AbstractItemResource extends AbstractResource implements
+    ItemResourceConstants {
+
+    private static Logger log = Logger.getLogger(AbstractItemResource.class);
+
+    protected final Item item;
+
+    /**
+     * Create a new <code>AbstractItemResource</code>.
+     *
+     * @param locator
+     * @param session
+     */
+    AbstractItemResource(DavResourceLocator locator, DavSession session, DavResourceFactory factory, Item item) {
+        super(locator, session, factory);
+        this.item = item;
+    }
+
+    //----------------------------------------------< DavResource interface >---
+    /**
+     * @see org.apache.jackrabbit.webdav.DavResource#getComplianceClass()
+     */
+    public String getComplianceClass() {
+        return ItemResourceConstants.COMPLIANCE_CLASS;
+    }
+
+    /**
+     * @see org.apache.jackrabbit.webdav.DavResource#getSupportedMethods()
+     */
+    public String getSupportedMethods() {
+        return ItemResourceConstants.METHODS;
+    }
+
+    /**
+     * Returns true if there exists a {@link Item repository item} with the given
+     * resource path, false otherwise.
+     *
+     * @see DavResource#exists()
+     */
+    public boolean exists() {
+        return item != null;
+    }
+
+    /**
+     * @see DavResource#getDisplayName() )
+     */
+    public String getDisplayName() {
+        String name = null;
+        if (exists()) {
+            try {
+                name = item.getName();
+            } catch (RepositoryException e) {
+                // ignore: should not occure
+                log.warn(e.getMessage());
+            }
+        }
+        String resPath = getResourcePath();
+        if (name == null && resPath != null) {
+            int pos = resPath.lastIndexOf('/');
+            if (pos>=0) {
+                name = resPath.substring(pos+1);
+            } else {
+                name = resPath;
+            }
+            // note: since index info is present only with existing resources
+            // there is no need to check for any '[index]' suffix.
+        }
+        return name;
+    }
+
+    /**
+     * Returns the resource representing the parent item of the repository item
+     * represented by this resource. If this resoure represents the root item
+     * a {@link RootCollection} is returned.
+     *
+     * @return the collection this resource is internal member of. Except for the
+     * repository root, the returned collection always represent the parent
+     * repository node.
+     * @see DavResource#getCollection()
+     */
+    public DavResource getCollection() {
+        DavResource collection = null;
+
+        String resourcePath = getResourcePath();
+        // No special treatment for the root-item needed, because this is
+        // covered by the RootItemCollection itself.
+        String parentResourcePath = Text.getRelativeParent(resourcePath, 1);
+        String parentWorkspacePath = getLocator().getWorkspacePath();
+
+        DavResourceLocator parentLoc = getLocator().getFactory().createResourceLocator(getLocator().getPrefix(), parentWorkspacePath, parentResourcePath);
+        try {
+            collection = createResourceFromLocator(parentLoc);
+        } catch (DavException e) {
+            log.error("Unexpected error while retrieving collection: " + e.getMessage());
+        }
+
+        return collection;
+    }
+
+    /**
+     * Moves the underlaying repository item to the indicated destination.
+     *
+     * @param destination
+     * @throws DavException
+     * @see DavResource#move(DavResource)
+     * @see Session#move(String, String)
+     */
+    public void move(DavResource destination) throws DavException {
+        if (!exists()) {
+            throw new DavException(DavServletResponse.SC_NOT_FOUND);
+        }
+        DavResourceLocator destPath = destination.getLocator();
+        if (!getLocator().isSameWorkspace(destPath)) {
+            throw new DavException(DavServletResponse.SC_FORBIDDEN);
+        }
+
+        try {
+            getRepositorySession().move(getResourcePath(), destination.getResourcePath());
+            complete();
+
+        } catch (PathNotFoundException e) {
+            // according to rfc 2518
+            throw new DavException(DavServletResponse.SC_CONFLICT, e.getMessage());
+        } catch (RepositoryException e) {
+            throw new JcrDavException(e);
+        }
+    }
+
+    /**
+     * Copies the underlaying repository item to the indicated destination. If
+     * the locator of the specified destination resource indicates a different
+     * workspace, {@link Workspace#copy(String, String, String)} is used to perform
+     * the copy operation, {@link Workspace#copy(String, String)} otherwise.
+     * <p/>
+     * Note, that this implementation does not support shallow copy.
+     *
+     * @param destination
+     * @param shallow
+     * @throws DavException
+     * @see DavResource#copy(DavResource, boolean)
+     * @see Workspace#copy(String, String)
+     * @see Workspace#copy(String, String, String)
+     */
+    public void copy(DavResource destination, boolean shallow) throws DavException {
+        if (!exists()) {
+            throw new DavException(DavServletResponse.SC_NOT_FOUND);
+        }
+        // TODO: support shallow and deep copy is required by RFC 2518
+        if (shallow) {
+            throw new DavException(DavServletResponse.SC_FORBIDDEN, "Unable to perform shallow copy.");
+        }
+
+        if (!(destination instanceof AbstractItemResource)) {
+            throw new DavException(DavServletResponse.SC_FORBIDDEN, "Cannot copy a resource that does not represent a repository item.");
+        }
+
+        try {
+            AbstractItemResource destResource = (AbstractItemResource) destination;
+            String destResourcePath = destResource.getResourcePath();
+            Workspace workspace = getRepositorySession().getWorkspace();
+            if (getLocator().isSameWorkspace(destination.getLocator())) {
+                workspace.copy(getResourcePath(), destResourcePath);
+            } else {
+                Workspace destWorkspace = destResource.getRepositorySession().getWorkspace();
+                destWorkspace.copy(workspace.getName(), getResourcePath(), destResourcePath);
+            }
+        } catch (PathNotFoundException e) {
+            // according to RFC 2518, should not occur
+            throw new DavException(DavServletResponse.SC_NOT_FOUND, e.getMessage());
+        } catch (RepositoryException e) {
+            throw new JcrDavException(e);
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Initialize the {@link org.apache.jackrabbit.webdav.lock.SupportedLock} property
+     * with entries that are valid for any type item resources.
+     *
+     * @see org.apache.jackrabbit.webdav.lock.SupportedLock
+     * @see org.apache.jackrabbit.webdav.transaction.TxLockEntry
+     * @see AbstractResource#initLockSupport()
+     */
+    protected void initLockSupport() {
+        if (exists()) {
+            // add supportedlock entries for local and eventually for global transaction locks
+            supportedLock.addEntry(new TxLockEntry(true));
+            supportedLock.addEntry(new TxLockEntry(false));
+        }
+    }
+
+    /**
+     * Define the set of reports supported by this resource.
+     *
+     * @see org.apache.jackrabbit.webdav.version.report.SupportedReportSetProperty
+     * @see AbstractResource#initSupportedReports()
+     */
+    protected void initSupportedReports() {
+        if (exists()) {
+            supportedReports = new SupportedReportSetProperty(new ReportType[] {
+                ReportType.EXPAND_PROPERTY,
+                NodeTypesReport.NODETYPES_REPORT,
+                ExportViewReport.EXPORTVIEW_REPORT,
+                LocateByUuidReport.LOCATE_BY_UUID_REPORT,
+		RegisteredNamespacesReport.REGISTERED_NAMESPACES_REPORT
+            });
+        }
+    }
+
+    /**
+     * Fill the property set for this resource.
+     */
+    protected void initProperties() {
+        super.initProperties();
+        if (exists()) {
+            try {
+                properties.add(new DefaultDavProperty(JCR_NAME, item.getName()));
+                properties.add(new DefaultDavProperty(JCR_PATH, item.getPath()));
+                properties.add(new DefaultDavProperty(JCR_DEPTH, String.valueOf(item.getDepth())));
+            } catch (RepositoryException e) {
+                log.error("Error while accessing jcr properties: " + e.getMessage());
+            }
+
+            // transaction resource additional protected properties
+            if (item.isNew()) {
+                properties.add(new DefaultDavProperty(JCR_ISNEW, null, true));
+            } else if (item.isModified()) {
+                properties.add(new DefaultDavProperty(JCR_ISMODIFIED, null, true));
+            }
+        }
+    }
+
+    /**
+     * @return href of the workspace or <code>null</code> if this resource
+     * does not represent a repository item.
+     *
+     * @see AbstractResource#getWorkspaceHref()
+     */
+    protected String getWorkspaceHref() {
+        String workspaceHref = null;
+	DavResourceLocator locator = getLocator();
+        if (locator != null && locator.getWorkspaceName() != null) {
+            workspaceHref = locator.getHref(isCollection());
+            if (locator.getResourcePath() != null) {
+                workspaceHref = workspaceHref.substring(workspaceHref.indexOf(locator.getResourcePath()));
+            }
+        }
+	log.info(workspaceHref);
+        return workspaceHref;
+    }
+
+    /**
+     * If this resource exists but does not contain a transaction id, complete
+     * will try to persist any modifications prsent on the underlaying repository
+     * item.
+     *
+     * @throws DavException if calling {@link Item#save()} fails
+     */
+    void complete() throws DavException {
+        if (exists() && getTransactionId() == null) {
+            try {
+                if (item.isModified()) {
+                    item.save();
+                }
+            } catch (RepositoryException e) {
+                // this includes LockException, ConstraintViolationException etc. not detected before
+                log.error("Error while completing request: " + e.getMessage() +" -> reverting changes.");
+                try {
+                    item.refresh(false);
+                } catch (RepositoryException re) {
+                    log.error("Error while reverting changes: " + re.getMessage());
+                }
+                throw new JcrDavException(e);
+            }
+        }
+    }
+
+    /**
+     * Build a new {@link DavResourceLocator} from the given repository item.
+     *
+     * @param repositoryItem
+     * @return a new locator for the specified item.
+     * @see #getLocatorFromResourcePath(String)
+     */
+    protected DavResourceLocator getLocatorFromItem(Item repositoryItem) {
+        String itemPath = null;
+        try {
+            if (repositoryItem != null) {
+                itemPath = repositoryItem.getPath();
+            }
+        } catch (RepositoryException e) {
+            // ignore: should not occur
+            log.warn(e.getMessage());
+        }
+        return getLocatorFromResourcePath(itemPath);
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/AbstractItemResource.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message