[CalendarServer-changes] [9793] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Sat Sep 8 14:45:32 PDT 2012


Revision: 9793
          http://trac.macosforge.org/projects/calendarserver/changeset/9793
Author:   cdaboo at apple.com
Date:     2012-09-08 14:45:32 -0700 (Sat, 08 Sep 2012)
Log Message:
-----------
Remove minidom use and replace with ElementTree.

Modified Paths:
--------------
    CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py
    CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py
    CalendarServer/trunk/txdav/xml/__init__.py
    CalendarServer/trunk/txdav/xml/parser.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py
    CalendarServer/trunk/txdav/xml/parser_etree.py
    CalendarServer/trunk/txdav/xml/rfc6578.py

Removed Paths:
-------------
    CalendarServer/trunk/txdav/xml/draft_sync.py

Modified: CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twext/web2/dav/test/test_xattrprops.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -201,7 +201,7 @@
         self._setValue(document, originalValue)
         self._checkValue(document)
         self.assertEquals(
-            decompress(self._getValue(document)), originalValue)
+            decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
 
 
     def test_getUpgradeCompressedPickle(self):
@@ -214,7 +214,7 @@
         self._setValue(document, compress(dumps(document)))
         self._checkValue(document)
         self.assertEquals(
-            decompress(self._getValue(document)), document.toxml())
+            decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
 
 
     def test_getInvalid(self):
@@ -243,7 +243,7 @@
         document = self._makeValue()
         self.propertyStore.set(document.root_element)
         self.assertEquals(
-            decompress(self._getValue(document)), document.toxml())
+            decompress(self._getValue(document)), document.root_element.toxml(pretty=False))
 
 
     def test_delete(self):
@@ -396,7 +396,7 @@
             document = self._makeValue(uid)
             self.propertyStore.set(document.root_element, uid=uid)
             self.assertEquals(
-                decompress(self._getValue(document, uid)), document.toxml())
+                decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
 
     def test_delete_uids(self):
         """
@@ -415,7 +415,7 @@
                     continue
                 document = self._makeValue(uid)
                 self.assertEquals(
-                    decompress(self._getValue(document, uid)), document.toxml())
+                    decompress(self._getValue(document, uid)), document.root_element.toxml(pretty=False))
         
     def test_contains_uids(self):
         """

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlaccountsparser.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -23,14 +23,13 @@
     "XMLAccountsParser",
 ]
 
-import xml.dom.minidom
-
 from twext.python.filepath import CachingFilePath as FilePath
 
 from twext.python.log import Logger
 
 from twistedcaldav.directory.directory import DirectoryService
 from twistedcaldav.directory.util import normalizeUUID
+from twistedcaldav.xmlutil import readXML
 
 import re
 import hashlib
@@ -88,15 +87,10 @@
             self.items[recordType] = {}
 
         # Read in XML
-        fd = open(self.xmlFile.path, "r")
-        doc = xml.dom.minidom.parse(fd)
-        fd.close()
-
-        # Verify that top-level element is correct
-        accounts_node = doc._get_documentElement()
-        if accounts_node._get_localName() != ELEMENT_ACCOUNTS:
-            log.error("Ignoring file %r because it is not a repository builder file" % (self.xmlFile,))
-            return
+        try:
+            _ignore_tree, accounts_node = readXML(self.xmlFile.path, ELEMENT_ACCOUNTS)
+        except ValueError, e:
+            log.error("XML parse error for '%s' because: %s" % (self.xmlFile, e,), raiseException=RuntimeError)
         self._parseXML(accounts_node)
 
     def _parseXML(self, node):
@@ -104,8 +98,7 @@
         Parse the XML root node from the accounts configuration document.
         @param node: the L{Node} to parse.
         """
-        if node.hasAttribute(ATTRIBUTE_REALM):
-            self.realm = node.getAttribute(ATTRIBUTE_REALM).encode("utf-8")
+        self.realm = node.get(ATTRIBUTE_REALM, "").encode("utf-8")
 
         def updateMembership(group):
             # Update group membership
@@ -114,20 +107,13 @@
                 if item is not None:
                     item.groups.add(group.shortNames[0])
 
-        for child in node._get_childNodes():
-            child_name = child._get_localName()
-            if child_name is None:
-                continue
-
+        for child in node.getchildren():
             try:
-                recordType = RECORD_TYPES[child_name]
+                recordType = RECORD_TYPES[child.tag]
             except KeyError:
-                raise RuntimeError("Unknown account type: %s" % (child_name,))
+                raise RuntimeError("Unknown account type: %s" % (child.tag,))
 
-            if child.hasAttribute(ATTRIBUTE_REPEAT):
-                repeat = int(child.getAttribute(ATTRIBUTE_REPEAT))
-            else:
-                repeat = 0
+            repeat = int(child.get(ATTRIBUTE_REPEAT, 0))
 
             principal = XMLAccountRecord(recordType)
             principal.parseXML(child)
@@ -249,58 +235,39 @@
         return result
 
     def parseXML(self, node):
-        for child in node._get_childNodes():
-            child_name = child._get_localName()
-            if child_name is None:
-                continue
-            elif child_name == ELEMENT_SHORTNAME:
-                if child.firstChild is not None:
-                    self.shortNames.append(child.firstChild.data.encode("utf-8"))
-            elif child_name == ELEMENT_GUID:
-                if child.firstChild is not None:
-                    self.guid = normalizeUUID(
-                        child.firstChild.data.encode("utf-8")
-                    )
-                    if len(self.guid) < 4:
-                        self.guid += "?" * (4 - len(self.guid))
-            elif child_name == ELEMENT_PASSWORD:
-                if child.firstChild is not None:
-                    self.password = child.firstChild.data.encode("utf-8")
-            elif child_name == ELEMENT_NAME:
-                if child.firstChild is not None:
-                    self.fullName = child.firstChild.data.encode("utf-8")
-            elif child_name == ELEMENT_FIRST_NAME:
-                if child.firstChild is not None:
-                    self.firstName = child.firstChild.data.encode("utf-8")
-            elif child_name == ELEMENT_LAST_NAME:
-                if child.firstChild is not None:
-                    self.lastName = child.firstChild.data.encode("utf-8")
-            elif child_name == ELEMENT_EMAIL_ADDRESS:
-                if child.firstChild is not None:
-                    self.emailAddresses.add(child.firstChild.data.encode("utf-8").lower())
-            elif child_name == ELEMENT_MEMBERS:
+        for child in node.getchildren():
+            if child.tag == ELEMENT_SHORTNAME:
+                self.shortNames.append(child.text.encode("utf-8"))
+            elif child.tag == ELEMENT_GUID:
+                self.guid = normalizeUUID(child.text.encode("utf-8"))
+                if len(self.guid) < 4:
+                    self.guid += "?" * (4 - len(self.guid))
+            elif child.tag == ELEMENT_PASSWORD:
+                self.password = child.text.encode("utf-8")
+            elif child.tag == ELEMENT_NAME:
+                self.fullName = child.text.encode("utf-8")
+            elif child.tag == ELEMENT_FIRST_NAME:
+                self.firstName = child.text.encode("utf-8")
+            elif child.tag == ELEMENT_LAST_NAME:
+                self.lastName = child.text.encode("utf-8")
+            elif child.tag == ELEMENT_EMAIL_ADDRESS:
+                self.emailAddresses.add(child.text.encode("utf-8").lower())
+            elif child.tag == ELEMENT_MEMBERS:
                 self._parseMembers(child, self.members)
-            elif child_name == ELEMENT_EXTRAS:
+            elif child.tag == ELEMENT_EXTRAS:
                 self._parseExtras(child, self.extras)
             else:
-                raise RuntimeError("Unknown account attribute: %s" % (child_name,))
+                raise RuntimeError("Unknown account attribute: %s" % (child.tag,))
 
         if not self.shortNames:
             self.shortNames.append(self.guid)
 
     def _parseMembers(self, node, addto):
-        for child in node._get_childNodes():
-            if child._get_localName() == ELEMENT_MEMBER:
-                if child.hasAttribute(ATTRIBUTE_RECORDTYPE):
-                    recordType = child.getAttribute(ATTRIBUTE_RECORDTYPE).encode("utf-8")
-                else:
-                    recordType = DirectoryService.recordType_users
-                if child.firstChild is not None:
-                    addto.add((recordType, child.firstChild.data.encode("utf-8")))
+        for child in node.getchildren():
+            if child.tag == ELEMENT_MEMBER:
+                recordType = child.get(ATTRIBUTE_RECORDTYPE, DirectoryService.recordType_users)
+                addto.add((recordType, child.text.encode("utf-8")))
 
     def _parseExtras(self, node, addto):
-        for child in node._get_childNodes():
-            key = child._get_localName()
-            if key:
-                value = child.firstChild.data.encode("utf-8")
-                addto[key.encode("utf-8")] = value
+        for child in node.getchildren():
+            addto[child.tag] = child.text.encode("utf-8")

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischeduleservers.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -20,9 +20,8 @@
 
 from twistedcaldav.config import config, fullServerPath
 from twistedcaldav.scheduling.delivery import DeliveryService
+from twistedcaldav.xmlutil import readXML
 
-import xml.dom.minidom
-
 """
 XML based iSchedule configuration file handling.
 """
@@ -100,15 +99,11 @@
         self.servers = []
         
         # Read in XML
-        fd = open(xmlFile.path, "r")
-        doc = xml.dom.minidom.parse(fd)
-        fd.close()
+        try:
+            _ignore_tree, servers_node = readXML(xmlFile.path, ELEMENT_SERVERS)
+        except ValueError:
+            log.error("Ignoring file %r because it is not a server-to-server config file" % (xmlFile,))
 
-        # Verify that top-level element is correct
-        servers_node = doc._get_documentElement()
-        if servers_node._get_localName() != ELEMENT_SERVERS:
-            log.error("Ignoring file %r because it is not a server-to-server config file" % (self.xmlFile,))
-            return
         self._parseXML(servers_node)
         
     def _parseXML(self, node):
@@ -116,12 +111,8 @@
         Parse the XML root node from the server-to-server configuration document.
         @param node: the L{Node} to parse.
         """
-
-        for child in node._get_childNodes():
-            child_name = child._get_localName()
-            if child_name is None:
-                continue
-            elif child_name == ELEMENT_SERVER:
+        for child in node.getchildren():
+            if child.tag == ELEMENT_SERVER:
                 self.servers.append(IScheduleServerRecord())
                 self.servers[-1].parseXML(child)
                 
@@ -147,49 +138,39 @@
             self._parseDetails()
 
     def parseXML(self, node):
-        for child in node._get_childNodes():
-            child_name = child._get_localName()
-            if child_name is None:
-                continue
-            elif child_name == ELEMENT_URI:
-                if child.firstChild is not None:
-                    self.uri = child.firstChild.data.encode("utf-8")
-            elif child_name == ELEMENT_AUTHENTICATION:
+        for child in node.getchildren():
+            if child.tag == ELEMENT_URI:
+                self.uri = child.text
+            elif child.tag == ELEMENT_AUTHENTICATION:
                 self._parseAuthentication(child)
-            elif child_name == ELEMENT_ALLOW_REQUESTS_FROM:
+            elif child.tag == ELEMENT_ALLOW_REQUESTS_FROM:
                 self.allow_from = True
-            elif child_name == ELEMENT_ALLOW_REQUESTS_TO:
+            elif child.tag == ELEMENT_ALLOW_REQUESTS_TO:
                 self.allow_to = True
-            elif child_name == ELEMENT_DOMAINS:
+            elif child.tag == ELEMENT_DOMAINS:
                 self._parseList(child, ELEMENT_DOMAIN, self.domains)
-            elif child_name == ELEMENT_CLIENT_HOSTS:
+            elif child.tag == ELEMENT_CLIENT_HOSTS:
                 self._parseList(child, ELEMENT_HOST, self.client_hosts)
             else:
-                raise RuntimeError("[%s] Unknown attribute: %s" % (self.__class__, child_name,))
+                raise RuntimeError("[%s] Unknown attribute: %s" % (self.__class__, child.tag,))
         
         self._parseDetails()
 
     def _parseList(self, node, element_name, appendto):
-        for child in node._get_childNodes():
-            if child._get_localName() == element_name:
-                if child.firstChild is not None:
-                    appendto.append(child.firstChild.data.encode("utf-8"))
+        for child in node.getchildren():
+            if child.tag == element_name:
+                appendto.append(child.text)
 
     def _parseAuthentication(self, node):
-        if node.hasAttribute(ATTRIBUTE_TYPE):
-            atype = node.getAttribute(ATTRIBUTE_TYPE).encode("utf-8")
-            if atype != ATTRIBUTE_BASICAUTH:
-                return
-        else:
+        atype = node.getAttribute(ATTRIBUTE_TYPE, "")
+        if atype != ATTRIBUTE_BASICAUTH:
             return
 
-        for child in node._get_childNodes():
-            if child._get_localName() == ELEMENT_USER:
-                if child.firstChild is not None:
-                    user = child.firstChild.data.encode("utf-8")
-            elif child._get_localName() == ELEMENT_PASSWORD:
-                if child.firstChild is not None:
-                    password = child.firstChild.data.encode("utf-8")
+        for child in node.getchildren():
+            if child.tag == ELEMENT_USER:
+                user = child.text
+            elif child.tag == ELEMENT_PASSWORD:
+                password = child.text
         
         self.authentication = ("basic", user, password,)
 

Added: CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_ischeduleservers.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,49 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twisted.python.filepath import FilePath
+from twistedcaldav.scheduling.ischeduleservers import IScheduleServersParser
+import twistedcaldav.test.util
+
+class Test_IScheduleServersParser(twistedcaldav.test.util.TestCase):
+    """
+    Test L{IScheduleServersParser} implementation.
+    """
+
+    def test_readXML(self):
+
+        fp = FilePath(self.mktemp())
+        fp.open("w").write(
+"""<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE servers SYSTEM "servertoserver.dtd">
+<servers>
+  <server>
+    <uri>https://localhost:8543/inbox</uri>
+    <allow-requests-from/>
+    <allow-requests-to/>
+    <domains>
+        <domain>example.org</domain>
+    </domains>
+    <hosts>
+        <host>127.0.0.1</host>
+    </hosts>
+  </server>
+</servers>
+"""
+)
+        
+        parser = IScheduleServersParser(fp)
+        self.assertEqual(len(parser.servers), 1)

Modified: CalendarServer/trunk/txdav/xml/__init__.py
===================================================================
--- CalendarServer/trunk/txdav/xml/__init__.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/__init__.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -33,6 +33,7 @@
 See RFC 3744: http://www.ietf.org/rfc/rfc3744.txt (WebDAV ACLs)
 See RFC 4331: http://www.ietf.org/rfc/rfc4331.txt (WebDAV Quota)
 See RFC 5842: http://www.ietf.org/rfc/rfc5842.txt (WebDAV Bind)
+See RFC 6578: http://www.ietf.org/rfc/rfc6578.txt (WebDAV Sync)
 """
 
 __all__ = [
@@ -45,10 +46,10 @@
 import txdav.xml.rfc3253
 import txdav.xml.rfc3744
 import txdav.xml.rfc4331
+import txdav.xml.rfc5397
 import txdav.xml.rfc5842
-import txdav.xml.rfc5397
 import txdav.xml.rfc5995
-import txdav.xml.draft_sync
+import txdav.xml.rfc6578
 import txdav.xml.extensions
 
 txdav # Shhh pyflakes

Deleted: CalendarServer/trunk/txdav/xml/draft_sync.py
===================================================================
--- CalendarServer/trunk/txdav/xml/draft_sync.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/draft_sync.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -1,103 +0,0 @@
-##
-# Copyright (c) 2009-2012 Apple Computer, Inc. All rights reserved.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-# 
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-##
-
-"""
-draft-daboo-webdav-sync (Collection Synchronization for WebDAV) XML
-Elements
-
-This module provides XML element definitions for use with WebDAV
-Synchronization.
-
-See draft-daboo-webdav-sync: http://tools.ietf.org/html/draft-daboo-webdav-sync
-
-Last draft referenced: -08
-"""
-
-__all__ = []
-
-
-from txdav.xml.base import WebDAVElement, WebDAVTextElement, dav_namespace
-from txdav.xml.element import registerElement, registerElementClass
-from txdav.xml.rfc2518 import MultiStatus
-
-
- at registerElement
- at registerElementClass
-class SyncCollection (WebDAVElement):
-    """
-    DAV report used to retrieve specific calendar component items via
-    their URIs.
-    """
-    name = "sync-collection"
-
-    # To allow for an empty element in a supported-report-set property we need
-    # to relax the child restrictions
-    allowed_children = {
-        (dav_namespace, "sync-token"): (0, 1), # When used in the REPORT this is required
-        (dav_namespace, "sync-level"): (0, 1), # When used in the REPORT this is required
-        (dav_namespace, "prop"      ): (0, 1),
-    }
-
-    def __init__(self, *children, **attributes):
-        super(SyncCollection, self).__init__(*children, **attributes)
-
-        self.property = None
-        self.sync_token = None
-        self.sync_level = None
-
-        for child in self.children:
-            qname = child.qname()
-
-            if qname == (dav_namespace, "sync-token"):
-                self.sync_token = str(child)
-
-            elif qname == (dav_namespace, "sync-level"):
-                self.sync_level = str(child)
-
-            elif qname == (dav_namespace, "prop"):
-                if self.property is not None:
-                    raise ValueError("Only one of DAV:prop allowed")
-                self.property = child
-
-
- at registerElement
- at registerElementClass
-class SyncToken (WebDAVTextElement):
-    """
-    Synchronization token used in report and as a property.
-    """
-    name = "sync-token"
-    hidden = True
-    protected = True
-
-
- at registerElement
- at registerElementClass
-class SyncLevel (WebDAVTextElement):
-    """
-    Synchronization level used in report.
-    """
-    name = "sync-level"
-
-
-# Extend MultiStatus, to add sync-token
-MultiStatus.allowed_children[(dav_namespace, "sync-token")] = (0, 1)

Modified: CalendarServer/trunk/txdav/xml/parser.py
===================================================================
--- CalendarServer/trunk/txdav/xml/parser.py	2012-09-08 21:41:55 UTC (rev 9792)
+++ CalendarServer/trunk/txdav/xml/parser.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -32,4 +32,4 @@
     "WebDAVDocument",
 ]
 
-from txdav.xml.parser_sax import WebDAVDocument
+from txdav.xml.parser_etree import WebDAVDocument

Added: CalendarServer/trunk/txdav/xml/parser_etree.py
===================================================================
--- CalendarServer/trunk/txdav/xml/parser_etree.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/xml/parser_etree.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,155 @@
+##
+# Copyright (c) 2012 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+ElementTree implementation of XML parser/generator for WebDAV documents.
+"""
+
+__all__ = [
+    "WebDAVDocument",
+]
+
+from xml.etree.ElementTree import TreeBuilder, XMLParser, ParseError,\
+    _namespace_map
+from txdav.xml.base import WebDAVUnknownElement, PCDATAElement
+from txdav.xml.base import _elements_by_qname
+from txdav.xml.parser_base import AbstractWebDAVDocument
+
+def QNameSplit(qname):
+    return tuple(qname[1:].split("}", 1)) if "}" in qname else ("", qname,)
+
+class WebDAVContentHandler (TreeBuilder):
+
+    def __init__(self):
+        super(WebDAVContentHandler, self).__init__()
+        self._characterBuffer = None
+        
+        self.startDocument()
+
+    def startDocument(self):
+        self.stack = [{
+            "name"       : None,
+            "class"      : None,
+            "attributes" : None,
+            "children"   : [],
+        }]
+
+        # Keep a cache of the subclasses we create for unknown XML
+        # elements, so that we don't create multiple classes for the
+        # same element; it's fairly typical for elements to appear
+        # multiple times in a document.
+        self.unknownElementClasses = {}
+
+    def close(self):
+        top = self.stack[-1]
+
+        assert top["name"] is None
+        assert top["class"] is None
+        assert top["attributes"] is None
+        assert len(top["children"]) is 1, "Must have exactly one root element, got %d" % len(top["children"])
+
+        self.dom = WebDAVDocument(top["children"][0])
+        del(self.unknownElementClasses)
+        return self.dom
+
+    def data(self, data):
+        # Stash character data away in a list that we will "".join() when done
+        if self._characterBuffer is None:
+            self._characterBuffer = []
+        self._characterBuffer.append(data)
+
+    def start(self, tag, attrs):
+        name = QNameSplit(tag)
+
+        if self._characterBuffer is not None:
+            pcdata = PCDATAElement("".join(self._characterBuffer))
+            self.stack[-1]["children"].append(pcdata)
+            self._characterBuffer = None
+
+        # Need to convert a "full" namespace in an attribute QName to the form
+        # "%s:%s".
+        attributes_dict = {}
+        for aname, avalue in attrs.items():
+            anamespace, aname = QNameSplit(aname)
+            if anamespace:
+                anamespace = _namespace_map.get(anamespace, anamespace)
+                aname = "%s:%s" % (anamespace, aname,)
+            attributes_dict[aname] = avalue
+
+        tag_namespace, tag_name = name
+
+        if name in _elements_by_qname:
+            element_class = _elements_by_qname[name]
+        elif name in self.unknownElementClasses:
+            element_class = self.unknownElementClasses[name]
+        else:
+            def element_class(*args, **kwargs):
+                element = WebDAVUnknownElement(*args, **kwargs)
+                element.namespace = tag_namespace
+                element.name      = tag_name
+                return element
+            self.unknownElementClasses[name] = element_class
+
+        self.stack.append({
+            "name"       : name,
+            "class"      : element_class,
+            "attributes" : attributes_dict,
+            "children"   : [],
+        })
+
+    def end(self, tag):
+        name = QNameSplit(tag)
+
+        if self._characterBuffer is not None:
+            pcdata = PCDATAElement("".join(self._characterBuffer))
+            self.stack[-1]["children"].append(pcdata)
+            self._characterBuffer = None
+
+        # Pop the current element from the stack...
+        top = self.stack[-1]
+        del(self.stack[-1])
+
+        assert top["name"] == name, "Last item on stack is %s while closing %s" % (top["name"], name)
+
+        # ...then instantiate the element and add it to the parent's list of
+        # children.
+        element = top["class"](*top["children"], **top["attributes"])
+
+        self.stack[-1]["children"].append(element)
+
+
+class WebDAVDocument(AbstractWebDAVDocument):
+    @classmethod
+    def fromStream(cls, source):
+        parser  = XMLParser(target=WebDAVContentHandler())
+        try:
+            while 1:
+                data = source.read(65536)
+                if not data:
+                    break
+                parser.feed(data)
+        except ParseError, e:
+            raise ValueError(e)
+        return parser.close()
+        
+    def writeXML(self, output):
+        self.root_element.writeXML(output)

Copied: CalendarServer/trunk/txdav/xml/rfc6578.py (from rev 9752, CalendarServer/trunk/txdav/xml/draft_sync.py)
===================================================================
--- CalendarServer/trunk/txdav/xml/rfc6578.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/xml/rfc6578.py	2012-09-08 21:45:32 UTC (rev 9793)
@@ -0,0 +1,100 @@
+##
+# Copyright (c) 2009-2012 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+RFC 6578 (Collection Synchronization for WebDAV) XML Elements
+
+This module provides XML element definitions for use with WebDAV
+Synchronization.
+
+See RFC 6578: http://www.ietf.org/rfc/rfc6578.txt
+"""
+
+__all__ = []
+
+
+from txdav.xml.base import WebDAVElement, WebDAVTextElement, dav_namespace
+from txdav.xml.element import registerElement, registerElementClass
+from txdav.xml.rfc2518 import MultiStatus
+
+
+ at registerElement
+ at registerElementClass
+class SyncCollection (WebDAVElement):
+    """
+    DAV report used to retrieve specific calendar component items via
+    their URIs.
+    """
+    name = "sync-collection"
+
+    # To allow for an empty element in a supported-report-set property we need
+    # to relax the child restrictions
+    allowed_children = {
+        (dav_namespace, "sync-token"): (0, 1), # When used in the REPORT this is required
+        (dav_namespace, "sync-level"): (0, 1), # When used in the REPORT this is required
+        (dav_namespace, "prop"      ): (0, 1),
+    }
+
+    def __init__(self, *children, **attributes):
+        super(SyncCollection, self).__init__(*children, **attributes)
+
+        self.property = None
+        self.sync_token = None
+        self.sync_level = None
+
+        for child in self.children:
+            qname = child.qname()
+
+            if qname == (dav_namespace, "sync-token"):
+                self.sync_token = str(child)
+
+            elif qname == (dav_namespace, "sync-level"):
+                self.sync_level = str(child)
+
+            elif qname == (dav_namespace, "prop"):
+                if self.property is not None:
+                    raise ValueError("Only one of DAV:prop allowed")
+                self.property = child
+
+
+ at registerElement
+ at registerElementClass
+class SyncToken (WebDAVTextElement):
+    """
+    Synchronization token used in report and as a property.
+    """
+    name = "sync-token"
+    hidden = True
+    protected = True
+
+
+ at registerElement
+ at registerElementClass
+class SyncLevel (WebDAVTextElement):
+    """
+    Synchronization level used in report.
+    """
+    name = "sync-level"
+
+
+# Extend MultiStatus, to add sync-token
+MultiStatus.allowed_children[(dav_namespace, "sync-token")] = (0, 1)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120908/0382bfb5/attachment-0001.html>


More information about the calendarserver-changes mailing list