001package org.apache.jackrabbit.webdav;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements.  See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License.  You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
021import org.apache.jackrabbit.webdav.xml.DomUtil;
022import org.apache.jackrabbit.webdav.xml.ElementIterator;
023import org.apache.jackrabbit.webdav.xml.XmlSerializable;
024import org.w3c.dom.Document;
025import org.w3c.dom.Element;
026
027import java.util.Map;
028import java.util.LinkedHashMap;
029
030/**
031 * MultiStatus representing the content of a multistatus response body and
032 * allows to retrieve the Xml representation.
033 */
034public class MultiStatus
035    implements DavConstants, XmlSerializable
036{
037
038    /**
039     * Map collecting the responses for this multistatus, where every href must
040     * only occur one single time.
041     */
042    private Map<String, MultiStatusResponse> responses = new LinkedHashMap<String, MultiStatusResponse>();
043
044    /**
045     * A general response description at the multistatus top level is used to
046     * provide a general message describing the overarching nature of the response.
047     * If this value is available an application may use it instead of
048     * presenting the individual response descriptions contained within the
049     * responses.
050     */
051    private String responseDescription;
052
053    /**
054     * Add response(s) to this multistatus, in order to build a multistatus for
055     * responding to a PROPFIND request.
056     *
057     * @param resource The resource to add property from
058     * @param propNameSet The requested property names of the PROPFIND request
059     * @param propFindType
060     * @param depth
061     */
062    public void addResourceProperties( DavResource resource, DavPropertyNameSet propNameSet, int propFindType,
063                                       int depth )
064    {
065        addResponse( new MultiStatusResponse( resource, propNameSet, propFindType ) );
066        if ( depth > 0 && resource.isCollection() )
067        {
068            DavResourceIterator iter = resource.getMembers();
069            while ( iter.hasNext() )
070            {
071                addResourceProperties( iter.nextResource(), propNameSet, propFindType, depth - 1 );
072            }
073        }
074    }
075
076    /**
077     * Add response(s) to this multistatus, in order to build a multistatus e.g.
078     * in order to respond to a PROPFIND request. Please note, that in terms
079     * of PROPFIND, this method would correspond to a
080     * {@link DavConstants#PROPFIND_BY_PROPERTY} propfind type.
081     *
082     * @param resource The resource to add property from
083     * @param propNameSet The requested property names of the PROPFIND request
084     * @param depth
085     * @see #addResourceProperties(DavResource, DavPropertyNameSet, int, int) for
086     * the corresponding method that allows to specify the type explicitly.
087     */
088    public void addResourceProperties( DavResource resource, DavPropertyNameSet propNameSet, int depth )
089    {
090        addResourceProperties( resource, propNameSet, PROPFIND_BY_PROPERTY, depth );
091    }
092
093    /**
094     * Add response(s) to this multistatus, in order to build a multistatus
095     * as returned for COPY, MOVE, LOCK or DELETE requests resulting in an error
096     * with a resource other than the resource identified in the Request-URI.
097     *
098     * @param resource
099     * @param status
100     * @param depth
101     */
102    public void addResourceStatus( DavResource resource, int status, int depth )
103    {
104        addResponse( new MultiStatusResponse( resource.getHref(), status ) );
105        if ( depth > 0 && resource.isCollection() )
106        {
107            DavResourceIterator iter = resource.getMembers();
108            while ( iter.hasNext() )
109            {
110                addResourceStatus( iter.nextResource(), status, depth - 1 );
111            }
112        }
113    }
114
115    /**
116     * Add a <code>MultiStatusResponse</code> element to this <code>MultiStatus</code>
117     *
118     * @param response
119     */
120    public void addResponse( MultiStatusResponse response )
121    {
122        responses.put( response.getHref(), response );
123    }
124
125    /**
126     * Returns the multistatus responses present as array.
127     *
128     * @return array of all {@link MultiStatusResponse responses} present in this
129     * multistatus.
130     */
131    public MultiStatusResponse[] getResponses()
132    {
133        return (MultiStatusResponse[]) responses.values().toArray( new MultiStatusResponse[responses.size()] );
134    }
135
136    /**
137     * Set the response description.
138     *
139     * @param responseDescription
140     */
141    public void setResponseDescription( String responseDescription )
142    {
143        this.responseDescription = responseDescription;
144    }
145
146    /**
147     * Returns the response description.
148     *
149     * @return responseDescription
150     */
151    public String getResponseDescription()
152    {
153        return responseDescription;
154    }
155
156    /**
157     * Return the Xml representation of this <code>MultiStatus</code>.
158     *
159     * @return Xml document
160     * @param document
161     */
162    public Element toXml( Document document )
163    {
164        Element multistatus = DomUtil.createElement( document, XML_MULTISTATUS, NAMESPACE );
165        for ( MultiStatusResponse response :  responses.values() )
166        {
167            multistatus.appendChild( response.toXml( document ) );
168        }
169        if ( responseDescription != null )
170        {
171            Element respDesc =
172                DomUtil.createElement( document, XML_RESPONSEDESCRIPTION, NAMESPACE, responseDescription );
173            multistatus.appendChild( respDesc );
174        }
175        return multistatus;
176    }
177
178    /**
179     * Build a <code>MultiStatus</code> from the specified xml element.
180     *
181     * @param multistatusElement
182     * @return new <code>MultiStatus</code> instance.
183     * @throws IllegalArgumentException if the given document is <code>null</code>
184     * or does not provide the required element.
185     */
186    public static MultiStatus createFromXml( Element multistatusElement )
187    {
188        if ( !DomUtil.matches( multistatusElement, XML_MULTISTATUS, NAMESPACE ) )
189        {
190            throw new IllegalArgumentException( "DAV:multistatus element expected." );
191        }
192
193        MultiStatus multistatus = new MultiStatus();
194
195        ElementIterator it = DomUtil.getChildren( multistatusElement, XML_RESPONSE, NAMESPACE );
196        while ( it.hasNext() )
197        {
198            Element respElem = it.nextElement();
199            MultiStatusResponse response = MultiStatusResponse.createFromXml( respElem );
200            multistatus.addResponse( response );
201        }
202
203        // optional response description on the multistatus element
204        multistatus.setResponseDescription( DomUtil.getChildText( multistatusElement, XML_RESPONSEDESCRIPTION,
205                                                                  NAMESPACE ) );
206        return multistatus;
207    }
208}