1 #!/usr/bin/env python 2 3 """ 4 DOM macros for virtual libxml2mod node methods and properties. 5 """ 6 7 import xml.dom 8 import libxml2mod 9 10 class TemporaryNode: 11 def __init__(self, ns, name): 12 self._ns = ns 13 self.name = name 14 self.type = "attribute" 15 self.nodeValue = None 16 17 def ns(self): 18 return self._ns 19 20 # NOTE: libxml2 seems to use UTF-8 throughout. 21 22 def from_unicode(s): 23 if type(s) == type(u""): 24 return s.encode("utf-8") 25 else: 26 return s 27 28 def to_unicode(s): 29 if type(s) == type(""): 30 return unicode(s, encoding="utf-8") 31 else: 32 return s 33 34 def _get_prefix_and_localName(name): 35 t = name.split(":") 36 if len(t) == 1: 37 return None, name 38 elif len(t) == 2: 39 return t 40 else: 41 # NOTE: Should raise an exception. 42 return None, None 43 44 _nodeTypes = { 45 "attribute" : xml.dom.Node.ATTRIBUTE_NODE, 46 "comment" : xml.dom.Node.COMMENT_NODE, 47 "document_xml" : xml.dom.Node.DOCUMENT_NODE, 48 "doctype" : xml.dom.Node.DOCUMENT_TYPE_NODE, 49 "dtd" : xml.dom.Node.DOCUMENT_TYPE_NODE, # NOTE: Needs verifying. 50 "element" : xml.dom.Node.ELEMENT_NODE, 51 "entity" : xml.dom.Node.ENTITY_NODE, 52 "entity_ref" : xml.dom.Node.ENTITY_REFERENCE_NODE, 53 "notation" : xml.dom.Node.NOTATION_NODE, 54 "pi" : xml.dom.Node.PROCESSING_INSTRUCTION_NODE, 55 "text" : xml.dom.Node.TEXT_NODE 56 } 57 58 def Node_ownerDocument(node): 59 return libxml2mod.doc(node) 60 61 def Node_nodeType(node): 62 global _nodesTypes 63 return _nodeTypes[libxml2mod.type(node)] 64 65 def Node_childNodes(node): 66 67 # NOTE: Consider a generator instead. 68 69 child_nodes = [] 70 node = libxml2mod.children(node) 71 while node is not None: 72 child_nodes.append(node) 73 node = libxml2mod.next(node) 74 return child_nodes 75 76 def Node_attributes(node): 77 attributes = {} 78 node = libxml2mod.properties(node) 79 while node is not None: 80 ns = libxml2mod.xmlNodeGetNs(node) 81 if ns is not None: 82 attributes[(libxml2mod.xmlNodeGetContent(ns), libxml2mod.name(node))] = node 83 else: 84 attributes[(None, libxml2mod.name(node))] = node 85 node = libxml2mod.next(node) 86 return attributes 87 88 def Node_namespaceURI(node): 89 ns = libxml2mod.xmlNodeGetNs(node) 90 if ns is not None: 91 return to_unicode(libxml2mod.xmlNodeGetContent(ns)) 92 else: 93 return None 94 95 def Node_nodeValue(node): 96 return to_unicode(libxml2mod.xmlNodeGetContent(node)) 97 98 def Node_prefix(node): 99 ns = libxml2mod.xmlNodeGetNs(node) 100 if ns is not None: 101 return to_unicode(libxml2mod.name(ns)) 102 else: 103 return None 104 105 def Node_nodeName(node): 106 prefix = Node_prefix(node) 107 if prefix is not None: 108 return prefix + ":" + Node_localName(node) 109 else: 110 return Node_localName(node) 111 112 def Node_tagName(node): 113 if libxml2mod.type(node) == "element": 114 return Node_nodeName(node) 115 else: 116 return None 117 118 def Node_localName(node): 119 return to_unicode(libxml2mod.name(node)) 120 121 def Node_parentNode(node): 122 if libxml2mod.type(node) == "document_xml": 123 return None 124 else: 125 return libxml2mod.parent(node) 126 127 def Node_previousSibling(node): 128 if libxml2mod.prev(node) is not None: 129 return libxml2mod.prev(node) 130 else: 131 return None 132 133 def Node_nextSibling(node): 134 if libxml2mod.next(node) is not None: 135 return libxml2mod.next(node) 136 else: 137 return None 138 139 def Node_hasAttributeNS(node, ns, localName): 140 return Node_getAttributeNS(ns, localName) is not None 141 142 def Node_hasAttribute(node, name): 143 return Node_getAttribute(name) is not None 144 145 def Node_getAttributeNS(node, ns, localName): 146 return to_unicode(libxml2mod.xmlGetNsProp(node, localName, ns)) 147 148 def Node_getAttribute(node, name): 149 return to_unicode(libxml2mod.xmlGetProp(node, name)) 150 151 def Node_getAttributeNodeNS(node, ns, localName): 152 # NOTE: Needs verifying. 153 return libxml2mod.xmlGetNsProp(node, localName, ns) 154 155 def Node_getAttributeNode(node, name): 156 # NOTE: Needs verifying. 157 return libxml2mod.xmlGetProp(node, name) 158 159 def Node_setAttributeNS(node, ns, name, value): 160 # NOTE: Need to convert from Unicode. 161 ns, name, value = map(from_unicode, [ns, name, value]) 162 163 prefix, localName = _get_prefix_and_localName(name) 164 165 # NOTE: Might need to be xmlSetNsProp. 166 if prefix is not None: 167 libxml2mod.xmlNewNsProp(node, libxml2mod.xmlNewNs(node, ns, prefix), localName, value) 168 elif ns is not None and ns == libxml2mod.xmlNodeGetContent(libxml2mod.xmlNodeGetNs(node)): 169 libxml2mod.xmlNewNsProp(node, libxml2mod.xmlNodeGetNs(node), localName, value) 170 else: 171 # NOTE: Needs verifying: what should happen to the namespace? 172 # NOTE: This also catches the case where None is the element's 173 # NOTE: namespace and is also used for the attribute. 174 libxml2mod.xmlNewNsProp(node, None, localName, value) 175 176 def Node_setAttribute(node, name, value): 177 # NOTE: Need to convert from Unicode. 178 name, value = map(from_unicode, [name, value]) 179 180 libxml2mod.xmlSetProp(node, name, value) 181 182 def _add_node(node, tmp): 183 if tmp.ns is not None: 184 child = libxml2mod.xmlNewNsProp(node, None, Node_localName(tmp), None) 185 ns = libxml2mod.xmlNewNs(child, Node_namespaceURI(tmp), Node_prefix(tmp)) 186 libxml2mod.xmlNodeSetNs(child, ns) 187 else: 188 child = libxml2mod.xmlNewProp(node, libxml2mod.name(tmp)) 189 190 return child 191 192 def Node_setAttributeNodeNS(node, ns, name, attr): 193 # NOTE: Not actually putting the node on the element. 194 Node_setAttributeNS(node, ns, name, attr.nodeValue) # Node_nodeValue(attr) 195 196 def Node_setAttributeNode(node, name, attr): 197 # NOTE: Not actually putting the node on the element. 198 Node_setAttribute(node, name, attr.nodeValue) # Node_nodeValue(attr) 199 200 def Node_createElementNS(node, ns, name): 201 # NOTE: Need to convert from Unicode. 202 ns, name = map(from_unicode, [ns, name]) 203 204 prefix, localName = _get_prefix_and_localName(name) 205 new_node = libxml2mod.xmlNewNode(localName) 206 # NOTE: Does it make sense to set the namespace if it is empty? 207 if ns is not None: 208 new_ns = libxml2mod.xmlNewNs(new_node, ns, prefix) 209 libxml2mod.xmlSetNs(new_node, new_ns) 210 return new_node 211 212 def Node_createElement(node, name): 213 # NOTE: Need to convert from Unicode. 214 name = from_unicode(name) 215 216 new_node = libxml2mod.xmlNewNode(name) 217 return new_node 218 219 def Node_createAttributeNS(node, ns, name): 220 # NOTE: Need to convert from Unicode. 221 ns, name = map(from_unicode, [ns, name]) 222 223 prefix, localName = _get_prefix_and_localName(name) 224 # NOTE: Does it make sense to set the namespace if it is empty? 225 if ns is not None: 226 new_ns = libxml2mod.xmlNewNs(new_node, ns, prefix) 227 else: 228 new_ns = None 229 return TemporaryNode(new_ns, localName) 230 231 def Node_createAttribute(node, name): 232 # NOTE: Need to convert from Unicode. 233 name = from_unicode(name) 234 235 return TemporaryNode(None, name) 236 237 def Node_createTextNode(node, value): 238 # NOTE: Need to convert from Unicode. 239 value = from_unicode(value) 240 241 return libxml2mod.xmlNewText(value) 242 243 def Node_createComment(node, value): 244 # NOTE: Need to convert from Unicode. 245 value = from_unicode(value) 246 247 return libxml2mod.xmlNewComment(value) 248 249 def Node_insertBefore(node, tmp, oldNode): 250 if not isinstance(tmp, TemporaryNode): 251 return libxml2mod.xmlAddPrevSibling(oldNode, tmp) 252 else: 253 return None 254 255 def Node_replaceChild(node, tmp, oldNode): 256 if not isinstance(tmp, TemporaryNode): 257 return libxml2mod.xmlReplaceNode(oldNode, tmp) 258 else: 259 return None 260 261 def Node_appendChild(node, tmp): 262 return libxml2mod.xmlAddChild(node, tmp) 263 264 def Node_removeChild(node, child): 265 libxml2mod.unlinkNode(child) 266 267 def Node_xpath(node, expr, variables=None, namespaces=None): 268 context = libxml2mod.xmlXPathNewContext(Node_ownerDocument(node)) 269 libxml2mod.xmlXPathSetContextNode(context, node) 270 # NOTE: Discover namespaces from the node. 271 for prefix, ns in (namespaces or {}).items(): 272 libxml2mod.xmlXPathRegisterNs(context, prefix, ns) 273 # NOTE: May need to tidy up the context. 274 return libxml2mod.xmlXPathEval(context, expr) 275 276 # Utility functions. 277 278 def createDocumentType(localName, publicId, systemId): 279 return None 280 281 def createDocument(namespaceURI, localName, doctype): 282 # NOTE: Fixed to use version 1.0 only. 283 d = libxml2mod.xmlNewDoc("1.0") 284 if localName is not None: 285 root = Node_createElementNS(d, namespaceURI, localName) 286 Node_appendChild(d, root) 287 return d 288 289 def parse(stream_or_string): 290 if hasattr(stream_or_string, "read"): 291 stream = stream_or_string 292 return parseString(stream.read()) 293 else: 294 return parseFile(stream_or_string) 295 296 def parseFile(s): 297 # NOTE: Switching off validation and remote DTD resolution. 298 context = libxml2mod.xmlCreateFileParserCtxt(s) 299 libxml2mod.xmlParserSetValidate(context, 0) 300 libxml2mod.xmlCtxtUseOptions(context, 0) 301 libxml2mod.xmlParseDocument(context) 302 return libxml2mod.xmlParserGetDoc(context) 303 304 def parseString(s): 305 # NOTE: Switching off validation and remote DTD resolution. 306 context = libxml2mod.xmlCreateMemoryParserCtxt(s, len(s)) 307 libxml2mod.xmlParserSetValidate(context, 0) 308 libxml2mod.xmlCtxtUseOptions(context, 0) 309 libxml2mod.xmlParseDocument(context) 310 return libxml2mod.xmlParserGetDoc(context) 311 312 def parseURI(uri): 313 context = libxml2mod.xmlCreateURLParserCtxt(url) 314 libxml2mod.xmlParserSetValidate(context, 0) 315 libxml2mod.xmlCtxtUseOptions(context, 0) 316 libxml2mod.xmlParseDocument(context) 317 return libxml2mod.xmlParserGetDoc(context) 318 319 def toFile(doc, s): 320 libxml2mod.xmlSaveFile(s, doc) 321 322 # vim: tabstop=4 expandtab shiftwidth=4