1 #!/usr/bin/env python 2 3 """ 4 DOM wrapper around libxml2. 5 """ 6 7 import xml.dom 8 import libxml2 9 import sys 10 11 # NOTE: Consider a generator instead. 12 13 class NamedNodeMap(object): 14 15 def __init__(self, node): 16 self.node = node 17 18 def getNamedItem(self, name): 19 return self.node.getAttributeNode(name) 20 21 def getNamedItemNS(self, ns, localName): 22 return self.node.getAttributeNodeNS(ns, localName) 23 24 def setNamedItem(self, node): 25 self.node.setAttributeNode(node.name, node) 26 27 def setNamedItemNS(self, node): 28 self.node.setAttributeNodeNS(node.namespaceURI, node.localName, node) 29 30 def __getitem__(self, name): 31 return self.getNamedItem(name) 32 33 def __setitem__(self, name, node): 34 if name == node.nodeName: 35 self.setNamedItem(node) 36 else: 37 raise KeyError, name 38 39 def __delitem__(self, name): 40 # NOTE: To be implemented. 41 pass 42 43 def values(self): 44 attributes = [] 45 _attribute = self.node._node.properties 46 while _attribute is not None: 47 attributes.append(Node(_attribute, ownerElement=self.node)) 48 _attribute = _attribute.next 49 return attributes 50 51 def keys(self): 52 return [(attr.namespaceURI, attr.localName) for attr in self.values()] 53 54 def items(self): 55 return [((attr.namespaceURI, attr.localName), attr) for attr in self.values()] 56 57 def __repr__(self): 58 return str(self) 59 60 def __str__(self): 61 return "{%s}" % ",\n".join(["%s : %s" % (repr(key), repr(value)) for key, value in self.items()]) 62 63 def _get_prefix_and_localName(name): 64 t = name.split(":") 65 if len(t) == 1: 66 return None, name 67 elif len(t) == 2: 68 return t 69 else: 70 # NOTE: Should raise an exception. 71 return None, None 72 73 class TemporaryNode(object): 74 def __init__(self, ns, name, nodeType): 75 self.ns = ns 76 self.name = name 77 self.nodeType = nodeType 78 self.prefix, self.localName = _get_prefix_and_localName(self.name) 79 80 class TemporaryText(object): 81 def __init__(self, _text): 82 self.ns = self.name = self.prefix = self.localName = None 83 self.nodeType = xml.dom.Node.TEXT_NODE 84 self._text = _text 85 86 class Node(object): 87 88 _nodeTypes = { 89 "attribute" : xml.dom.Node.ATTRIBUTE_NODE, 90 "comment" : xml.dom.Node.COMMENT_NODE, 91 "document_xml" : xml.dom.Node.DOCUMENT_NODE, 92 "doctype" : xml.dom.Node.DOCUMENT_TYPE_NODE, 93 "dtd" : xml.dom.Node.DOCUMENT_TYPE_NODE, # NOTE: Needs verifying. 94 "element" : xml.dom.Node.ELEMENT_NODE, 95 "entity" : xml.dom.Node.ENTITY_NODE, 96 "entity_ref" : xml.dom.Node.ENTITY_REFERENCE_NODE, 97 "notation" : xml.dom.Node.NOTATION_NODE, 98 "pi" : xml.dom.Node.PROCESSING_INSTRUCTION_NODE, 99 "text" : xml.dom.Node.TEXT_NODE 100 } 101 102 def __init__(self, node, ownerElement=None, doctype=None): 103 self._node = node 104 self.ownerElement = ownerElement 105 self.doctype = doctype 106 107 def _ownerDocument(self): 108 return Node(self._node.doc) 109 110 def _nodeType(self): 111 return self._nodeTypes[self._node.type] 112 113 def _childNodes(self): 114 115 # NOTE: Consider a generator instead. 116 117 child_nodes = [] 118 _node = self._node.children 119 while _node is not None: 120 child_nodes.append(Node(_node)) 121 _node = _node.next 122 return child_nodes 123 124 def _attributes(self): 125 return NamedNodeMap(self) 126 127 def _getNs(self): 128 129 "Internal namespace information retrieval." 130 131 try: 132 return self._node.ns() 133 except libxml2.treeError: 134 return None 135 136 def _namespaceURI(self): 137 ns = self._getNs() 138 if ns is not None: 139 return ns.content 140 else: 141 return None 142 143 def _nodeValue(self): 144 return self._node.content 145 146 def _prefix(self): 147 ns = self._getNs() 148 if ns is not None: 149 return ns.name 150 else: 151 return None 152 153 def _nodeName(self): 154 prefix = self._prefix() 155 if prefix is not None: 156 return prefix + ":" + self._localName() 157 else: 158 return self._localName() 159 160 def _tagName(self): 161 if self._node.type == "element": 162 return self._nodeName() 163 else: 164 return None 165 166 def _localName(self): 167 return self._node.name 168 169 def _parentNode(self): 170 if self.nodeType == xml.dom.Node.DOCUMENT_NODE: 171 return None 172 else: 173 return Node(self._node.parent) 174 175 def hasAttributeNS(self, ns, localName): 176 return self._getAttributeNS(ns, localName) is not None 177 178 def hasAttribute(self, name): 179 return self._getAttribute(name) is not None 180 181 def getAttributeNS(self, ns, localName): 182 return self._getAttributeNS(ns, localName) or "" 183 184 def _getAttributeNS(self, ns, localName): 185 return self._node.nsProp(localName, ns) 186 187 def getAttribute(self, name): 188 return self._getAttribute(name) or "" 189 190 def _getAttribute(self, name): 191 return self._node.prop(name) 192 193 def getAttributeNodeNS(self, ns, localName): 194 return self.attributes[(ns, localName)] 195 196 def getAttributeNode(self, localName): 197 # NOTE: Needs verifying. 198 return self.attributes[(None, localName)] 199 200 def setAttributeNS(self, ns, name, value): 201 prefix, localName = _get_prefix_and_localName(name) 202 if prefix is not None: 203 self._node.setNsProp(self._node.newNs(ns, prefix), localName, value) 204 elif ns == self._node.ns().content: 205 self._node.setNsProp(self._node.ns(), localName, value) 206 else: 207 # NOTE: Needs verifying: what should happen to the namespace? 208 self._node.setNsProp(None, localName, value) 209 210 def setAttribute(self, name, value): 211 self._node.setProp(name, value) 212 213 def setAttributeNodeNS(self, ns, name, node): 214 # NOTE: Not actually putting the node on the element. 215 self.setAttributeNS(ns, name, node.nodeValue) 216 217 def setAttributeNode(self, name, node): 218 # NOTE: Not actually putting the node on the element. 219 self.setAttribute(name, node.nodeValue) 220 221 def createElementNS(self, ns, name): 222 prefix, localName = _get_prefix_and_localName(name) 223 _node = libxml2.newNode(localName) 224 _ns = _node.newNs(ns, prefix) 225 _node.setNs(_ns) 226 return Node(_node) 227 228 def createElement(self, name): 229 _node = libxml2.newNode(localName) 230 return Node(_node) 231 232 def createAttributeNS(self, ns, name): 233 prefix, localName = _get_prefix_and_localName(name) 234 return TemporaryNode(ns, name, xml.dom.Node.ATTRIBUTE_NODE) 235 236 def createAttribute(self, name): 237 return TemporaryNode(ns, name, xml.dom.Node.ATTRIBUTE_NODE) 238 239 def createTextNode(self, value): 240 return TemporaryText(self._node.doc.newDocText(value)) 241 242 def _add_node(self, tmp): 243 if tmp.nodeType == xml.dom.Node.ATTRIBUTE_NODE: 244 if tmp.ns is not None: 245 _child = self._node.newNsProp(None, tmp.localName, None) 246 _ns = _child.newNs(tmp.ns, tmp.prefix) 247 _child.setNs(_ns) 248 else: 249 _child = self._node.newProp(None, tmp.name, None) 250 else: 251 _child = None 252 253 return _child 254 255 def importNode(self, node, deep): 256 257 if node.nodeType == xml.dom.Node.ELEMENT_NODE: 258 imported_element = self.ownerDocument.createElementNS(node.namespaceURI, node.tagName) 259 for value in node.attributes.values(): 260 imported_element.setAttributeNS(value.namespaceURI, value.nodeName, value.nodeValue) 261 262 if deep: 263 for child in node.childNodes: 264 imported_child = self.importNode(child, deep) 265 if imported_child: 266 imported_element.appendChild(imported_child) 267 268 return imported_element 269 270 elif node.nodeType == xml.dom.Node.TEXT_NODE: 271 return self.ownerDocument.createTextNode(node.nodeValue) 272 273 elif node.nodeType == xml.dom.Node.ATTRIBUTE_NODE: 274 return self.ownerDocument.createAttributeNS(node.namespaceURI, node.name) 275 276 raise ValueError, node.nodeType 277 278 def insertBefore(self, tmp, oldNode): 279 if tmp.nodeType == xml.dom.Node.TEXT_NODE: 280 _child = tmp._text 281 elif tmp.nodeType == xml.dom.Node.ELEMENT_NODE: 282 _child = tmp._node 283 else: 284 _child = self._add_node(tmp) 285 _child.unlinkNode() 286 return Node(oldNode._node.addPrevSibling(_child)) 287 288 def replaceChild(self, tmp, oldNode): 289 if tmp.nodeType == xml.dom.Node.TEXT_NODE: 290 _child = tmp._text 291 elif tmp.nodeType == xml.dom.Node.ELEMENT_NODE: 292 _child = tmp._node 293 else: 294 _child = self._add_node(tmp) 295 _child.unlinkNode() 296 return Node(oldNode._node.replaceNode(_child)) 297 298 def appendChild(self, tmp): 299 if tmp.nodeType == xml.dom.Node.TEXT_NODE: 300 _child = self._node.addChild(tmp._text) 301 elif tmp.nodeType == xml.dom.Node.ELEMENT_NODE: 302 _child = self._node.addChild(tmp._node) 303 else: 304 _child = self._add_node(tmp) 305 return Node(_child) 306 307 #doctype defined in __init__ 308 #ownerElement defined in __init__ 309 ownerDocument = property(_ownerDocument) 310 childNodes = property(_childNodes) 311 value = data = nodeValue = property(_nodeValue) 312 name = nodeName = property(_nodeName) 313 tagName = property(_tagName) 314 namespaceURI = property(_namespaceURI) 315 prefix = property(_prefix) 316 localName = property(_localName) 317 parentNode = property(_parentNode) 318 nodeType = property(_nodeType) 319 attributes = property(_attributes) 320 321 def isSameNode(self, other): 322 return self._node.nodePath() == other._node.nodePath() 323 324 def __eq__(self, other): 325 return self._node.nodePath() == other._node.nodePath() 326 327 # Utility functions. 328 329 def createDocumentType(localName, publicId, systemId): 330 return None 331 332 def createDocument(namespaceURI, localName, doctype): 333 # NOTE: Fixed to use version 1.0 only. 334 d = Node(libxml2.newDoc("1.0"), doctype=doctype) 335 if localName is not None: 336 root = d.createElementNS(namespaceURI, localName) 337 d.appendChild(root) 338 return d 339 340 def parse(stream_or_string): 341 if hasattr(stream_or_string, "read"): 342 stream = stream_or_string 343 else: 344 stream = open(stream_or_string) 345 return parseString(stream.read()) 346 347 def parseString(s): 348 return Node(libxml2.parseDoc(s)) 349 350 def parseURI(uri): 351 return Node(libxml2.parseURI(uri)) 352 353 def toString(node): 354 return node._node.serialize() 355 356 def toStream(node, stream=None): 357 stream = stream or sys.stdout 358 stream.write(toString(node)) 359 360 # vim: tabstop=4 expandtab shiftwidth=4