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