[Zope-Checkins] CVS: Packages/webdav - xmltools.py:1.15.2.1

Andreas Jung andreas at andreas-jung.com
Sun Oct 31 13:51:01 EST 2004


Update of /cvs-repository/Packages/webdav
In directory cvs.zope.org:/tmp/cvs-serv32435/lib/python/webdav

Modified Files:
      Tag: Zope-2_7-branch
	xmltools.py 
Log Message:

      - Collector #1443: Applied patch by Simon Eisenmann that reimplements 
        the XML parser used in WebDAV fixing a memory leak.


=== Packages/webdav/xmltools.py 1.15 => 1.15.2.1 ===
--- Packages/webdav/xmltools.py:1.15	Tue Apr  8 14:48:22 2003
+++ Packages/webdav/xmltools.py	Sun Oct 31 13:51:01 2004
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
@@ -11,348 +11,94 @@
 #
 ##############################################################################
 
-"""WebDAV XML parsing tools. Note that this module does just
-   enough for the purposes of DAV - it is not intended as a
-   general xml toolkit, and will probably eventually go away
-   in favor of a standard xml package once some issues are
-   worked out."""
+
+""" 
+WebDAV XML request parsing tool using xml.minidom as xml parser.
+Code contributed by Simon Eisenmann, struktur AG, Stuttgart, Germany
+"""
 
 __version__='$Revision$'[11:-2]
 
-import sys, os
-import Shared.DC.xml.xmllib
-from Acquisition import Implicit
-
-
-type_document=0
-type_element=1
-type_attribute=2
-type_text=3
-type_cdata=4
-type_entityref=5
-type_entity=6
-type_procinst=7
-type_comment=8
-type_notation=9
-
-
-class Node(Implicit):
-    """Common base class for Node objects."""
-    __name__=''
-    __value__=''
-    __attrs__=[]
-    __nodes__=[]
-    __nskey__=''
-
-    def name(self):  return self.__name__
-    def attrs(self): return self.__attrs__
-    def value(self): return self.__value__
-    def nodes(self): return self.__nodes__
-    def nskey(self): return self.__nskey__
+"""
+TODO:
 
-    def addNode(self, node):
-        self.__nodes__.append(node.__of__(self))
+ - Check the methods Node.addNode, Node.remap and Node del_attr
+   and find out if some code uses/requires these methods.
 
-    def namespace(self):
-        nskey=self.__nskey__
-        while 1:
-            if hasattr(self, '__nsdef__'):
-                val=self.__nsdef__.get(nskey, None)
-                if val is not None: return val
-            if not hasattr(self, 'aq_parent'):
-                return ''
-            self=self.aq_parent
-
-    def elements(self, name=None, ns=None ):
-        nodes=[]
-        name=name and name.lower()
-        for node in self.__nodes__:
-            if node.__type__==type_element and \
-               ((name is None) or ((node.__name__.lower())==name)) and \
-               ((ns is None) or (node.namespace()==ns)):
-                nodes.append(node)
+   => If yes implement them, else forget them.
+   
+   NOTE: So far i didn't have any problems.
+         If you have problems please report them.
+
+"""
+
+from xml.dom import minidom
+
+class Node:
+    """ our nodes no matter what type """
+    
+    node = None
+    
+    def __init__(self, node):
+        self.node=node
+        
+    def elements(self, name=None, ns=None):
+        nodes=[ Node(n) for n in self.node.childNodes if n.nodeType == n.ELEMENT_NODE and \
+                                                   ((name is None) or ((n.localName.lower())==name)) and \
+                                                   ((ns is None) or (n.namespaceURI==ns)) ]
         return nodes
 
-    def __getitem__(self, n):
-        return self.__nodes__[n]
-
     def qname(self):
-        ns=self.__nskey__
-        if ns: ns='%s:' % ns
-        return '%s%s' % (ns, self.__name__)
+        return '%s%s' % (self.namespace(), self.name()) 
+        
+    def addNode(self, node):
+        # XXX: no support for adding nodes here
+        raise NotImplementedError, 'addNode not implemented'
 
     def toxml(self):
-        return self.__value__
-
+        return self.node.toxml()
+        
     def strval(self):
         return self.toxml()
-
-
-class Document(Node):
-    def __init__(self, encoding='utf-8', stdalone=''):
-        self.__name__ ='document'
-        self.__nodes__=[]
-        self.encoding=encoding
-        self.stdalone=stdalone
-        self.document=self
-
-    def toxml(self):
-        result=['<?xml version="1.0" encoding="%s"?>' % self.encoding]
-        for node in self.__nodes__:
-            result.append(node.toxml())
-        return ''.join(result)
-
-class Element(Node):
-    __type__=type_element
-
-    def __init__(self, name, attrs={}):
-        self.__name__ =name
-        self.__attrs__=[]
-        self.__nodes__=[]
-        self.__nsdef__={}
-        self.__nskey__=''
-        for name, val in attrs.items():
-            attr=Attribute(name, val)
-            self.__attrs__.append(attr)
-        self.ns_parse()
-        parts=self.__name__.split(':')
-        if len(parts) > 1:
-            self.__nskey__=parts[0]
-            self.__name__=':'.join(parts[1:])
-
-    def ns_parse(self):
-        nsdef=self.__nsdef__={}
-        for attr in self.attrs():
-            name, val=attr.name(), attr.value()
-            key=name.lower()
-            if key[:6]=='xmlns:':
-                nsdef[name[6:]]=val
-            elif key=='xmlns':
-                nsdef['']=val
-
-    def fixup(self):
-        self.__attrs__=map(lambda n, s=self: n.__of__(s), self.__attrs__)
-
-    def get_attr(self, name, ns=None, default=None):
-        for attr in self.__attrs__:
-            if attr.name()==name and (ns is None) or (ns==attr.namespace()):
-                return attr
-        return default
-
+        
+    def name(self):  return self.node.localName
+    def attrs(self): return self.node.attributes
+    def value(self): return self.node.nodeValue
+    def nodes(self): return self.node.childNodes
+    def nskey(self): return self.node.namespaceURI
+    
+    def namespace(self): return self.nskey()
+  
     def del_attr(self, name):
-        attrs=[]
-        for attr in self.__attrs__:
-            if attr.name() != name:
-                attrs.append(attr)
-        self.__attrs__=attrs
-
+        # XXX: no support for removing attributes 
+	#      zope can calls this after remapping to remove namespace
+	#      haven't seen this happening though
+        return None
+  
     def remap(self, dict, n=0, top=1):
-        # The remap method effectively rewrites an element and all of its
-        # children, consolidating namespace declarations into the element
-        # on which the remap function is called and fixing up namespace
-        # lookup structures.
-        nsval=self.namespace()
-        if not nsval: nsid=''
-        elif not dict.has_key(nsval):
-            nsid='ns%d' % n
-            dict[nsval]=nsid
-            n=n+1
-        else: nsid=dict[nsval]
-        for attr in self.__attrs__:
-            dict, n=attr.remap(dict, n, 0)
-        for node in self.elements():
-            dict, n=node.remap(dict, n, 0)
-        attrs=[]
-        for attr in self.__attrs__:
-            name=attr.__name__
-            if not (((len(name) >= 6) and (name[:6]=='xmlns:')) or \
-                    name=='xmlns'):
-                attrs.append(attr)
-        self.__attrs__=attrs
-        self.__nsdef__={}
-        self.__nskey__=nsid
-        if top:
-            attrs=self.__attrs__
-            keys=dict.keys()
-            keys.sort()
-            for key in keys:
-                attr=Attribute('xmlns:%s' % dict[key], key)
-                attrs.append(attr.__of__(self))
-            self.__attrs__=attrs
-            self.ns_parse()
-        return dict, n
-
-    def toxml(self):
-        qname=self.qname()
-        result=['<%s' % qname]
-        for attr in self.__attrs__:
-            result.append(attr.toxml())
-        if not self.__value__ and not self.__nodes__:
-            result.append('/>')
-        else:
-            result.append('>')
-            for node in self.__nodes__:
-                result.append(node.toxml())
-            result.append('</%s>' % qname)
-        return ''.join(result)
-
-    def strval(self, top=1):
-        if not self.__value__ and not self.__nodes__:
-            return ''
-        result=map(lambda n: n.toxml(), self.__nodes__)
-        return ''.join(result)
-
-class Attribute(Node):
-    __type__=type_attribute
-    def __init__(self, name, val):
-        self.__name__=name
-        self.__value__=val
-        self.__nskey__=''
-        parts=name.split(':')
-        if len(parts) > 1:
-            pre=parts[0].lower()
-            if not (pre in ('xml', 'xmlns')):
-                self.__nskey__=parts[0]
-                self.__name__=':'.join(parts[1:])
-
-    def remap(self, dict, n=0, top=1):
-        nsval=self.namespace()
-        if not nsval: nsid=''
-        elif not dict.has_key(nsval):
-            nsid='ns%d' % n
-            dict[nsval]=nsid
-            n=n+1
-        else: nsid=dict[nsval]
-        self.__nskey__=nsid
-        return dict, n
-
-    def toxml(self):
-        ns=self.__nskey__
-        if ns: ns='%s:' % ns
-        return ' %s%s="%s"' % (ns, self.__name__, self.__value__)
-
-class Text(Node):
-    __name__='#text'
-    __type__=type_text
-    def __init__(self, val):
-        self.__value__=val
-    def toxml(self):
-        return escape(self.__value__)
-
-class CData(Node):
-    __type__=type_cdata
-    __name__='#cdata'
-    def __init__(self, val):
-        self.__value__=val
-    def toxml(self):
-        return '<![CDATA[%s]]>' % self.__value__
-
-class EntityRef(Node):
-    __name__='#entityref'
-    __type__=type_entityref
-    def __init__(self, val):
-        self.__value__=val
-    def toxml(self):
-        return '&%s;' % self.__value__
-
-class Entity(Node):
-    __name__='#entity'
-    __type__=type_entity
-    def __init__(self, name, pubid, sysid, nname):
-        self.__value__=val
-    def toxml(self):
-        return ''
-
-class ProcInst(Node):
-    __type__=type_procinst
-    def __init__(self, name, val):
-        self.__name__=name
-        self.__value__=val
-    def toxml(self):
-        return '<?%s %s?>' % (self.__name__, self.__value__)
-
-class Comment(Node):
-    __name__='#comment'
-    __type__=type_comment
-    def __init__(self, val):
-        self.__value__=val
-    def toxml(self):
-        return '<!--%s-->' % self.__value__
-
+        # XXX: this method is used to do some strange remapping of elements
+        #      and namespaces .. not sure how to do this with minidom
+        #      and if this is even required for something
+	#      zope calls this to change namespaces in PropPatch and Lock
+        return {},0
+
+    def __repr__(self):
+        if self.namespace():
+            return "<Node %s (from %s)>" % (self.name(), self.namespace())
+        else: return "<Node %s>" % self.name()
+
+
+class XmlParser:
+    """ simple wrapper around minidom to support the required 
+        interfaces for zope.webdav
+    """
 
-
-
-
-class XmlParser(Shared.DC.xml.xmllib.XMLParser):
+    dom = None
+    
     def __init__(self):
-        Shared.DC.xml.xmllib.XMLParser.__init__(self)
-        self.root=None
-        self.node=None
-
-    def parse(self, data):
-        # prepending a XML preample to make xmllib happy
-        # (Collector #863)
-        if not data.startswith("<?xml"):   
-            data = '<?xml version="1.0" ?>\n' + data
-        self.feed(data)
-        self.close()
-        return self.root
-
-    def add(self, node):
-        self.node.addNode(node)
-
-    def push(self, node):
-        self.node.addNode(node)
-        self.node=self.node.__nodes__[-1]
-
-    def pop(self):
-        self.node=self.node.aq_parent
-
-    def unknown_starttag(self, name, attrs):
-        node=Element(name, attrs)
-        self.push(node)
-        # Fixup aq chain!
-        self.node.fixup()
-
-    def unknown_endtag(self, name):
-        self.pop()
-
-    def handle_xml(self, encoding, stdalone):
-        self.root=Document(encoding, stdalone)
-        self.node=self.root
-
-    def handle_doctype(self, tag, pubid, syslit, data):
         pass
-
-    def handle_entity(self, name, strval, pubid, syslit, ndata):
-        self.add(Entity(name, strval, pubid, syslit, ndata))
-
-    def handle_cdata(self, data):
-        self.add(CData(data))
-
-    def handle_proc(self, name, data):
-        self.add(ProcInst(name, data))
-
-    def handle_comment(self, data):
-        self.add(Comment(data))
-
-    def handle_data(self, data):
-        self.add(Text(data))
-
-    def unknown_entityref(self, data):
-        self.add(EntityRef(data))
-
-
-
-def escape(data, rmap={}):
-    data=data.replace( "&", "&amp;")
-    data=data.replace( "<", "&lt;")
-    data=data.replace( ">", "&gt;")
-    for key, val in rmap.items():
-        data=data.replace( key, val)
-    return data
-
-def remap(data, dict={'DAV:': 'd'}):
-    root=XmlParser().parse(data)
-    root.elements()[0].remap(dict, 0)
-    return root.toxml()
+        
+    def parse(self, data):
+        self.dom=minidom.parseString(data)
+        return Node(self.dom)
+        



More information about the Zope-Checkins mailing list