Friday May 27, 2011

100% XML CardDAV query

The CARDDAV:address-data request XML Element allows a client to specify in which format it wishes the address book resources to be returned via the content-type and version XML parameters (See draft-ietf-vcarddav-carddav-10#section-10.4 ).

This, coupled with the soon to be published xCard format (http://tools.ietf.org/html/draft-ietf-vcarddav-vcardxml), can be used to request a 100% XML CardDAV query response: Instead of making a request using:

 <CARDDAV:address-data/>

the client can use something like:

 <CARDDAV:address-data content-type="application/vcard+xml" version="4.0" />

The complete  Request/Response looks like:

>> Request <<


   REPORT /home/bernard/addressbook/ HTTP/1.1
   Host: addressbook.example.com
   Depth: 1
   Content-Type: text/xml; charset="utf-8"
   Content-Length: xxxx

   <?xml version="1.0" encoding="utf-8" ?>
   <C:addressbook-query xmlns:D="DAV:"
                     xmlns:C="urn:ietf:params:xml:ns:carddav">
     <D:prop>
       <D:getetag/>
       <C:address-data content-type="application/vcard+xml" version="4.0"/>
     </D:prop>
     <C:filter/>
   </C:addressbook-query>

>> Response <<


   HTTP/1.1 207 Multi-Status
   Date: Sat, 11 Nov 2006 09:32:12 GMT
   Content-Type: text/xml; charset="utf-8"
   Content-Length: xxxx

   <?xml version="1.0" encoding="utf-8" ?>
   <D:multistatus xmlns:D="DAV:"
                  xmlns:C="urn:ietf:params:xml:ns:carddav">
     <D:response>
       <D:href>/home/bernard/addressbook/v102.vcf</D:href>
       <D:propstat>
         <D:prop>
           <D:getetag>"23ba4d-ff11fb"</D:getetag>
           <C:address-data content-type="application/vcard+xml" version="4.0">
             <vcards xmlns="urn:ietf:params:xml:ns:vcard-4.0">
               <vcard>
                 <fn><text>J. Doe</text></fn>
                 <uid><uri>xxx:12</uri></uid>
                 <email>
                   <parameters><type><text>work</text></type></parameters>
                   <text>john.doe@example.ca</text>
                 </email>
               </vcard>
             </vcards>
           </C:address-data>
         </D:prop>
         <D:status>HTTP/1.1 200 OK</D:status>
       </D:propstat>
     </D:response>
   </D:multistatus>

Now if you combine the above with the use of the "X-HTTP-Method-Override" HTTP header to wrap the REPORT using HTTP POST, you get an "almost browser friendly" CardDAV query.

Monday Dec 01, 2008

iCalendar to XML conversion using ical4j

The ical4j library is probably the reference java library for manipulating iCalendar data. It offers a full representation of the iCalendar (RFC 2445) data model (calendar components, properties and parameters), a parser, as well a set of helper classes to do date/time calculations.

A nice design point is that there is a good separation between the act of parsing an iCalendar stream and the building of ical4j objects.

This is achieved by using an event driven model where the parser calls back a handler object whenever a new event (beginning/end of a component or property,...) is encountered by the parser. In other words, the parser is the iCalendar equivalent of an XML SAX parser.

Here is the interface that a handler has to implement:

public interface ContentHandler {

    /\*\*
     \* Triggers the start of handling a calendar.
     \*/
    void startCalendar();

    /\*\*
     \* Triggers the end of handling a calendar.
     \*/
    void endCalendar();

    /\*\*
     \* Triggers the start of handling a component.
     \*/
    void startComponent(String name);

    /\*\*
     \* Triggers the end of handling a component.
     \*/
    void endComponent(String name);

    /\*\*
     \* Triggers the start of handling a property.
     \*/
    void startProperty(String name);

    /\*\*
     \* Triggers the handling of a property value.
     \*/
    void propertyValue(String value) throws URISyntaxException, ParseException,
            IOException;

    /\*\*
     \* Triggers the end of handling a property.
     \*/
    void endProperty(String name);

    /\*\*
     \* Triggers the handling of a parameter.
     \*/
    void parameter(String name, String value) throws URISyntaxException;
}

And here is a sample class implementing this interface to produce a very basic iCalendar to XML conversion (using the StAX XML API):

public final class XMLHandler implements ContentHandler {  
    
    private final XMLStreamWriter xmlWriter;
    
    public XMLHandler(XMLStreamWriter xmlWriter) {
        this.xmlWriter = xmlWriter;
    }
    
    /\*\*
     \* {@inheritDoc}
     \*/
    public void startCalendar() {
        writeStartElement("vcalendar");
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endCalendar() {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void startComponent(String name) {
        writeStartElement(name);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endComponent(String name) {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void startProperty(String name) {
        writeStartElement(name);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void propertyValue(String value) throws URISyntaxException, ParseException,
            IOException {
        // would need unwrapping
        writeCharacters(value);
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void endProperty(String name) {
        writeEndElement();
    }

    /\*\*
     \* {@inheritDoc}
     \*/
    public void parameter(String name, String value) throws URISyntaxException {
        writeAttribute(name, value);
    }
    
    private void writeStartElement(String name) {
        try {
            xmlWriter.writeStartElement(name.toLowerCase());
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeCharacters(String value) {
        try {
            xmlWriter.writeCharacters(value);
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeAttribute(String name, String value) {
        try {
            xmlWriter.writeAttribute(name.toLowerCase(), value);
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }
    
    private void writeEndElement() {
        try {
            xmlWriter.writeEndElement();
            xmlWriter.writeCharacters("\\r\\n");
        } catch (XMLStreamException xe) {
            throw new IllegalStateException("got xml error while writing", xe);
        }
    }

Finally, a sample program making use of this handler, along with the program output:

public final class Main {
    
    public static final String ICALSTREAM =
            "BEGIN:VCALENDAR\\r\\nPRODID:-//Sun/Sample//EN\\r\\nVERSION:2.0\\r\\n" + 
            "BEGIN:VEVENT\\r\\nUID:1\\r\\nDTSTAMP:20070313T082041Z\\r\\nDTSTART;VALUE=DATE:20081212\\r\\n" +
            "SUMMARY:wrapped \\r\\n" +
            " summary\\r\\n" +
            "END:VEVENT\\r\\nEND:VCALENDAR";
    /\*\*
     \* @param args the command line arguments
     \*/
    public static void main(String[] args) throws Exception {
        
        Reader reader = new UnfoldingReader(new StringReader(ICALSTREAM));
        
        CalendarParser parser = CalendarParserFactory.getInstance().createParser();
        
        StringWriter writer = new StringWriter();
        XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer);
        XMLHandler handler = new XMLHandler(xmlWriter);
        xmlWriter.writeStartDocument();
        parser.parse(reader, handler);
        xmlWriter.writeEndDocument();
        xmlWriter.close();
        
        System.out.println("xml representation:" + writer.toString());
    }
}
 
java ical2xml.Main
xml representation:<?xml version="1.0" ?><vcalendar><prodid>-//Sun/Sample//EN</prodid>
<version>2.0</version>
<vevent><uid>1</uid>
<dtstamp>20070313T082041Z</dtstamp>
<dtstart value="DATE">20081212</dtstart>
<summary>wrapped summary</summary>
</vevent>
</vcalendar>

The generated XML is totally non standard (Calconnect is currently defining such a standard) and quite ugly but it shows how the parser can be used to do lightweight processing of an iCalendar stream without going through the intermediate step of creating a full ical4j Calendar object (which can be quite expensive in terms of CPU and memory).

The same technique could be used to generate a json output or to translate an iCalendar stream into a different object model.

About

arnaudq

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks