/*
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.webdav.lib.methods;
import java.io.InputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Locale;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.httpclient.State;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
//import org.apache.commons.httpclient.log.Log;
//import org.apache.commons.httpclient.log.LogSource;
import org.apache.webdav.lib.Property;
import org.apache.webdav.lib.BaseProperty;
import org.apache.webdav.lib.ResponseEntity;
import org.apache.webdav.lib.properties.*;
import org.apache.util.WebdavStatus;
import org.apache.util.DOMUtils;
import org.apache.util.DOMWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Utility class for XML response parsing.
*
* @author B.C. Holmes
* @author Remy Maucherat
* @author Dirk Verbeeck
*/
public class XMLResponseMethodBase
extends HttpMethodBase {
//static private final Log log = LogSource.getInstance(XMLResponseMethodBase.class.getName());
// ----------------------------------------------------------- Constructors
/**
* Method constructor.
*/
public XMLResponseMethodBase() {
super();
}
/**
* Method constructor.
*/
public XMLResponseMethodBase(String path) {
super(path);
}
// ----------------------------------------------------- Instance Variables
/**
* Response document.
*/
private Document responseDocument = null;
/**
* Document builder.
*/
protected DocumentBuilder builder = null;
/**
* Hashtable of response nodes
*/
private Hashtable responseHashtable = null;
// ------------------------------------------------------------- Properties
/**
* Response document getter.
*
* @return Document response document
*/
public Document getResponseDocument() {
return this.responseDocument;
}
/**
* Return an enumeration containing the responses.
*
* @return An enumeration containing objects implementing the
* ResponseEntity interface
*/
public Enumeration getResponses() {
return getResponseHashtable().elements();
}
protected State getState() {
return state;
}
// --------------------------------------------------- WebdavMethod Methods
/**
* Debug property setter.
*
* @param int Debug
*/
/*
public void setDebug(int debug) {
super.setDebug(debug);
log.setLevel(debug);
}
*/
/**
* Reset the State of the class to its initial state, so that it can be
* used again.
*/
public void recycle() {
super.recycle();
responseHashtable = null;
}
/**
* Parse response.
*
* @param input Input stream
*/
public void parseResponse(InputStream input)
throws IOException, HttpException {
if (getStatusCode() == WebdavStatus.SC_MULTI_STATUS) {
parseXMLResponse(input);
}
}
protected void parseXMLResponse(InputStream input)
throws IOException, HttpException {
if (builder == null) {
try {
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new HttpException
("XML Parser Configuration error: " + e.getMessage());
}
}
try {
responseDocument = builder.parse(new InputSource(input));
} catch (Exception e) {
throw new IOException
("XML parsing error; response stream is not valid XML: "
+ e.getMessage());
}
// init the response table to display the responses during debugging
if (debug > 10) {
//if (log.isDebugEnabled()) {
initResponseHashtable();
}
}
protected Hashtable getResponseHashtable() {
checkUsed();
if (responseHashtable == null) {
initResponseHashtable();
}
return responseHashtable;
}
private synchronized void initResponseHashtable() {
if (responseHashtable == null) {
responseHashtable = new Hashtable();
int status = getStatusCode();
if (status == WebdavStatus.SC_MULTI_STATUS) {
Element multistatus =
getResponseDocument().getDocumentElement();
NodeList list = multistatus.getChildNodes();
if (list != null) {
for (int i = 0; i < list.getLength(); i++) {
try {
Element child = (Element) list.item(i);
String name = DOMUtils.getElementLocalName(child);
String namespace = DOMUtils.getElementNamespaceURI
(child);
if (Response.TAG_NAME.equals(name) &&
"DAV:".equals(namespace)) {
Response response =
new ResponseWithinMultistatus(child);
responseHashtable.put(response.getHref(),
response);
if (debug>10)
System.out.println(response);
//log.debug(response);
}
} catch (ClassCastException e) {
}
}
}
} else if (responseDocument != null) {
Response response = new SingleResponse(responseDocument,
getPath(), status);
responseHashtable.put(response.getHref(), response);
if (debug>10)
System.out.println(response);
//log.debug(response);
}
}
}
/**
* This method creates a property implementation from an element.
* It treats known properties (i.e., the DAV properties) specially.
* These properties are instantiated as an implementation from the
* <code>org.apache.webdav.lib.properties</code> package.
*/
protected static Property convertElementToProperty(
Response response, Element element) {
Property property = null;
String namespace = DOMUtils.getElementNamespaceURI(element);
// handle DAV properties specially
if (namespace != null && namespace.equals("DAV:")) {
String localName = DOMUtils.getElementLocalName(element);
if (ResourceTypeProperty.TAG_NAME.equals(localName)) {
property = new ResourceTypeProperty(response, element);
} else if (GetLastModifiedProperty.TAG_NAME.equals(localName)) {
property = new GetLastModifiedProperty(response, element);
} else if (CurrentUserPrivilegeSetProperty.TAG_NAME.equals
(localName)) {
property =
new CurrentUserPrivilegeSetProperty(response, element);
} else if (LockDiscoveryProperty.TAG_NAME.equals(localName)) {
property = new LockDiscoveryProperty(response, element);
} else if (SupportedLockProperty.TAG_NAME.equals(localName)) {
property = new SupportedLockProperty(response, element);
} else if (AclProperty.TAG_NAME.equals(localName)) {
property = new AclProperty(response, element);
} else if (PrincipalCollectionSetProperty.TAG_NAME.equals(localName)) {
property = new PrincipalCollectionSetProperty(response, element);
}
}
if (property == null) {
property = new BaseProperty(response, element);
}
return property;
}
// ---------------------------------------------------------- Inner Classes
/**
* An abstract class that models a DAV:response.
*/
public abstract class Response implements ResponseEntity {
protected Node node = null;
Response(Node node) {
this.node = node;
}
public static final String TAG_NAME = "response";
public abstract int getStatusCode();
public abstract String getHref();
public Enumeration getProperties() {
NodeList list =
DOMUtils.getElementsByTagNameNS(node, "prop", "DAV:");
Vector vector = new Vector();
for (int i = 0; list != null && i < list.getLength(); i++ ) {
Element element = (Element) list.item(i);
NodeList children = element.getChildNodes();
for (int j = 0; children != null && j < children.getLength();
j++) {
try {
Element child = (Element) children.item(j);
vector.addElement(XMLResponseMethodBase.
convertElementToProperty(this, child));
} catch (ClassCastException e) {
}
}
}
return vector.elements();
}
public String toString () {
StringWriter tmp = new StringWriter();
DOMWriter domWriter = new DOMWriter(tmp, true);
domWriter.print(node);
return tmp.getBuffer().toString();
}
}
/**
* A class that models the DAV:response element within a multistatus.
*/
class ResponseWithinMultistatus extends Response {
public ResponseWithinMultistatus(Element element) {
super(element);
}
public int getStatusCode() {
// The status element for the response can be inside the propstat element
// or directly inside the response element.
// <multistatus xmlns=\DAV:\>
// <response>
// <href>/slide/files/</href>
// <propstat>
// <prop><displayname>files</displayname></prop>
// <status>HTTP/1.1 200 OK</status>
// </propstat>
// </response>
// </multistatus>
Element propstat = getFirstElement("DAV:", "propstat");
if (propstat != null ) {
Element status = DOMUtils.getFirstElement(propstat,"DAV:", "status");
if (status != null) {
return DOMUtils.parseStatus(DOMUtils.getTextValue(status));
}
}
// <multistatus xmlns=\DAV:\>
// <response>
// <href>/slide/files/</href>
// <href>/slide/files/a</href>
// <status>HTTP/1.1 200 OK</status>
// </response>
// </multistatus>
Element status = getFirstElement("DAV:", "status");
if (status != null) {
return DOMUtils.parseStatus(DOMUtils.getTextValue(status));
}
return -1;
}
public String getHref() {
Element href = getFirstElement("DAV:", "href");
if (href != null) {
return getState().URLDecode(DOMUtils.getTextValue(href));
} else {
return "";
}
}
protected Element getFirstElement(String namespace, String name) {
return DOMUtils.getFirstElement(this.node, namespace, name);
}
}
class SingleResponse extends Response {
private int statusCode = -1;
private String href = null;
SingleResponse(Document document, String href, int statusCode) {
super(document);
this.statusCode = statusCode;
this.href = href;
}
public int getStatusCode() {
return this.statusCode;
}
public String getHref() {
return this.href;
}
}
}