All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.caldav.ExpandPropertyReport Maven / Gradle / Ivy

Go to download

Milton Enterprise: Supports DAV level 2 and above, including Caldav and Carddav. Available on AGPL or commercial licenses

There is a newer version: 4.0.5.2400
Show newest version
/*
 * Copyright 2012 McEvoy Software Ltd.
 */

package io.milton.http.caldav;

import io.milton.http.ResourceFactory;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.report.Report;
import io.milton.http.values.HrefList;
import io.milton.http.values.PropFindResponseList;
import io.milton.http.values.ValueAndType;
import io.milton.http.webdav.PropFindPropertyBuilder;
import io.milton.http.webdav.PropFindResponse;
import io.milton.http.webdav.PropFindXmlGenerator;
import io.milton.http.webdav.PropertiesRequest;
import io.milton.http.webdav.PropertiesRequest.Property;
import io.milton.http.webdav.WebDavProtocol;
import io.milton.resource.PropFindableResource;
import io.milton.resource.Resource;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.namespace.QName;
import org.jdom2.Document;
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * http://tools.ietf.org/html/rfc3253#section-3.8
 * 
 * Many property values are defined as a DAV:href, or a set of DAV:href
elements.  The DAV:expand-property report provides a mechanism for
retrieving in one request the properties from the resources
identified by those DAV:href elements.  This report not only
decreases the number of requests required, but also allows the server
to minimize the number of separate read transactions required on the
underlying versioning store.

The DAV:expand-property report SHOULD be supported by all resources
that support the REPORT method.

Marshalling:

The request body MUST be a DAV:expand-property XML element.




name value: a property element type

namespace value: an XML namespace

The response body for a successful request MUST be a
DAV:multistatus XML element.

multistatus: see RFC 2518, Section 12.9

The properties reported in the DAV:prop elements of the
DAV:multistatus element MUST be those identified by the
DAV:property elements in the DAV:expand-property element.  If
there are DAV:property elements nested within a DAV:property
element, then every DAV:href in the value of the corresponding
property is replaced by a DAV:response element whose DAV:prop
elements report the values of the properties identified by the
nested DAV:property elements.  The nested DAV:property elements
can in turn contain DAV:property elements, so that multiple levels
of DAV:href expansion can be requested.

Note that a validating parser MUST be aware that the DAV:expand-
property report effectively modifies the DTD of every property by
replacing every occurrence of "href" in the DTD with "href |
response".
 * 
 *  REPORT /foo.html HTTP/1.1
Host: www.webdav.org
Content-Type: text/xml; charset="utf-8"
Content-Length: xxxx











>>RESPONSE

HTTP/1.1 207 Multi-Status
Content-Type: text/xml; charset="utf-8"
Content-Length: xxxx




http://www.webdav.org/foo.html




http://repo.webdav.org/his/23




http://repo.webdav.org/his/23/ver/1


Fred
 
 http://www.webdav.org/ws/dev/sally 
  
HTTP/1.1 200 OK
 

http://repo.webdav.org/his/23/ver/2


Sally

http://repo.webdav.org/act/add-refresh-cmd
 
HTTP/1.1 200 OK
 
 
HTTP/1.1 200 OK
 
 
HTTP/1.1 200 OK
 


In this example, the DAV:creator-displayname and DAV:activity-set
properties of the versions in the DAV:version-set of the
DAV:version-history of http://www.webdav.org/foo.html are reported.
 *
 * @author bradm
 */
public class ExpandPropertyReport implements Report {

	private static final Logger log = LoggerFactory.getLogger(MultiGetReport.class);
	private final ResourceFactory resourceFactory;
	private final PropFindPropertyBuilder propertyBuilder;
	private final PropFindXmlGenerator xmlGenerator;

	public ExpandPropertyReport(ResourceFactory resourceFactory, PropFindPropertyBuilder propertyBuilder, PropFindXmlGenerator xmlGenerator) {
		this.resourceFactory = resourceFactory;
		this.propertyBuilder = propertyBuilder;
		this.xmlGenerator = xmlGenerator;
	}

	@Override
	public String process(String host, String path, Resource calendar, Document doc) throws NotAuthorizedException, BadRequestException {
		log.debug("process");

		PropertiesRequest parseResult = parse(doc.getRootElement());
		
		List propFindResponses;
		try {
			PropFindableResource pfr = (PropFindableResource) calendar;
			propFindResponses = propertyBuilder.buildProperties(pfr, 1, parseResult, path);

			for (PropFindResponse r : propFindResponses) {
				Set> set = r.getKnownProperties().entrySet();
				set = new HashSet<>(set);
				for (Entry p : set) {
					Object val = p.getValue().getValue();
					QName name = p.getKey();
					if (val instanceof HrefList) {
						HrefList hrefList = (HrefList) val;
						Property prop = parseResult.get(name);
						PropFindResponseList propFindResponseList = toResponseList(host, hrefList, prop);
						replaceHrefs(host, propFindResponseList, prop);
						r.getKnownProperties().remove(name);
						r.getKnownProperties().put(name, new ValueAndType(propFindResponseList, PropFindResponseList.class));
					}
				}
			}
		} catch (URISyntaxException ex) {
			throw new RuntimeException("Exception parsing url, indicating the requested URL is not correctly encoded. Please check the client application.", ex);
		}

		//show("",propFindResponses);

		return xmlGenerator.generate(propFindResponses);
	}

	public PropertiesRequest parse(Element elProp) {
		Set set = new HashSet<>();
		for (Object o : elProp.getChildren()) {
			if (o instanceof Element) {
				Element el = (Element) o;
				if (el.getName().equals("property")) {
					QName name = getQName(el);
					Set nested = parseChildren(el);
					Property p = new Property(name, nested);
					set.add(p);
				}
			}
		}
		return new PropertiesRequest(set);
	}

	private Set parseChildren(Element elProp) {
		Set set = new HashSet<>();
		for (Object o : elProp.getChildren()) {
			if (o instanceof Element) {
				Element el = (Element) o;
				if (el.getName().equals("property")) {
					QName name = getQName(el);					
					Set nested = parseChildren(el);
					Property p = new Property(name, nested);
					set.add(p);
				}
			}
		}
		return set;
	}

	private QName getQName(Element el) {
		String local = el.getAttributeValue("name");
		String ns = el.getAttributeValue("namespace");
		if (ns == null) {
			ns = WebDavProtocol.DAV_URI;
		}
		return new QName(ns, local);
	}

	@Override
	public String getName() {
		return "expand-property";
	}

	private PropFindResponseList toResponseList(String host, HrefList hrefList, Property prop) throws URISyntaxException, NotAuthorizedException, BadRequestException {
		PropFindResponseList list = new PropFindResponseList();
		for (String href : hrefList) {
			Resource r = resourceFactory.getResource(host, href);
			if (r != null) {
				if (r instanceof PropFindableResource) {
					PropFindableResource pfr = (PropFindableResource) r;
					PropertiesRequest propertyRequest = new PropertiesRequest(prop.getNested());
					List propFindResponses = propertyBuilder.buildProperties(pfr, 0, propertyRequest, href);
					// should be only one
					list.addAll(propFindResponses);
				}
			}
		}
		return list;
	}

	private void replaceHrefs(String host, PropFindResponseList propFindResponseList, Property prop) throws URISyntaxException, NotAuthorizedException, BadRequestException {		
		for (PropFindResponse r : propFindResponseList) {
			Set> set = r.getKnownProperties().entrySet();
			set = new HashSet<>(set);
			for (Entry p : set) {
				Object val = p.getValue().getValue();
				QName name = p.getKey();
				if (val instanceof HrefList) {
					HrefList hrefList = (HrefList) val;
					Property nestedProp = prop.getNestedMap().get(name);
					// Check for another level of nesting
					if (nestedProp != null && nestedProp.getNested() != null && !nestedProp.getNested().isEmpty())
					{
						PropFindResponseList nestedList = toResponseList(host, hrefList, nestedProp);
						replaceHrefs(host, nestedList, nestedProp);
						r.getKnownProperties().remove(name);
						r.getKnownProperties().put(name, new ValueAndType(nestedList, PropFindResponseList.class));
					}
				}
			}
		}
	}

	private void show(String prefix, List propFindResponses) {
		for( PropFindResponse p : propFindResponses ) {
			for( Entry e : p.getKnownProperties().entrySet()) {
				Object o = e.getValue().getValue();
				if( o instanceof PropFindResponseList) {
					PropFindResponseList childList = (PropFindResponseList) o;
					show(prefix + "   ", childList);
				} else {
					
				}
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy