1from __future__ import absolute_import, division, unicode_literals 2 3 4from collections import MutableMapping 5from xml.dom import minidom, Node 6import weakref 7 8from . import base 9from .. import constants 10from ..constants import namespaces 11from .._utils import moduleFactoryFactory 12 13 14def getDomBuilder(DomImplementation): 15 Dom = DomImplementation 16 17 class AttrList(MutableMapping): 18 def __init__(self, element): 19 self.element = element 20 21 def __iter__(self): 22 return iter(self.element.attributes.keys()) 23 24 def __setitem__(self, name, value): 25 if isinstance(name, tuple): 26 raise NotImplementedError 27 else: 28 attr = self.element.ownerDocument.createAttribute(name) 29 attr.value = value 30 self.element.attributes[name] = attr 31 32 def __len__(self): 33 return len(self.element.attributes) 34 35 def items(self): 36 return list(self.element.attributes.items()) 37 38 def values(self): 39 return list(self.element.attributes.values()) 40 41 def __getitem__(self, name): 42 if isinstance(name, tuple): 43 raise NotImplementedError 44 else: 45 return self.element.attributes[name].value 46 47 def __delitem__(self, name): 48 if isinstance(name, tuple): 49 raise NotImplementedError 50 else: 51 del self.element.attributes[name] 52 53 class NodeBuilder(base.Node): 54 def __init__(self, element): 55 base.Node.__init__(self, element.nodeName) 56 self.element = element 57 58 namespace = property(lambda self: hasattr(self.element, "namespaceURI") and 59 self.element.namespaceURI or None) 60 61 def appendChild(self, node): 62 node.parent = self 63 self.element.appendChild(node.element) 64 65 def insertText(self, data, insertBefore=None): 66 text = self.element.ownerDocument.createTextNode(data) 67 if insertBefore: 68 self.element.insertBefore(text, insertBefore.element) 69 else: 70 self.element.appendChild(text) 71 72 def insertBefore(self, node, refNode): 73 self.element.insertBefore(node.element, refNode.element) 74 node.parent = self 75 76 def removeChild(self, node): 77 if node.element.parentNode == self.element: 78 self.element.removeChild(node.element) 79 node.parent = None 80 81 def reparentChildren(self, newParent): 82 while self.element.hasChildNodes(): 83 child = self.element.firstChild 84 self.element.removeChild(child) 85 newParent.element.appendChild(child) 86 self.childNodes = [] 87 88 def getAttributes(self): 89 return AttrList(self.element) 90 91 def setAttributes(self, attributes): 92 if attributes: 93 for name, value in list(attributes.items()): 94 if isinstance(name, tuple): 95 if name[0] is not None: 96 qualifiedName = (name[0] + ":" + name[1]) 97 else: 98 qualifiedName = name[1] 99 self.element.setAttributeNS(name[2], qualifiedName, 100 value) 101 else: 102 self.element.setAttribute( 103 name, value) 104 attributes = property(getAttributes, setAttributes) 105 106 def cloneNode(self): 107 return NodeBuilder(self.element.cloneNode(False)) 108 109 def hasContent(self): 110 return self.element.hasChildNodes() 111 112 def getNameTuple(self): 113 if self.namespace is None: 114 return namespaces["html"], self.name 115 else: 116 return self.namespace, self.name 117 118 nameTuple = property(getNameTuple) 119 120 class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable 121 def documentClass(self): 122 self.dom = Dom.getDOMImplementation().createDocument(None, None, None) 123 return weakref.proxy(self) 124 125 def insertDoctype(self, token): 126 name = token["name"] 127 publicId = token["publicId"] 128 systemId = token["systemId"] 129 130 domimpl = Dom.getDOMImplementation() 131 doctype = domimpl.createDocumentType(name, publicId, systemId) 132 self.document.appendChild(NodeBuilder(doctype)) 133 if Dom == minidom: 134 doctype.ownerDocument = self.dom 135 136 def elementClass(self, name, namespace=None): 137 if namespace is None and self.defaultNamespace is None: 138 node = self.dom.createElement(name) 139 else: 140 node = self.dom.createElementNS(namespace, name) 141 142 return NodeBuilder(node) 143 144 def commentClass(self, data): 145 return NodeBuilder(self.dom.createComment(data)) 146 147 def fragmentClass(self): 148 return NodeBuilder(self.dom.createDocumentFragment()) 149 150 def appendChild(self, node): 151 self.dom.appendChild(node.element) 152 153 def testSerializer(self, element): 154 return testSerializer(element) 155 156 def getDocument(self): 157 return self.dom 158 159 def getFragment(self): 160 return base.TreeBuilder.getFragment(self).element 161 162 def insertText(self, data, parent=None): 163 data = data 164 if parent != self: 165 base.TreeBuilder.insertText(self, data, parent) 166 else: 167 # HACK: allow text nodes as children of the document node 168 if hasattr(self.dom, '_child_node_types'): 169 # pylint:disable=protected-access 170 if Node.TEXT_NODE not in self.dom._child_node_types: 171 self.dom._child_node_types = list(self.dom._child_node_types) 172 self.dom._child_node_types.append(Node.TEXT_NODE) 173 self.dom.appendChild(self.dom.createTextNode(data)) 174 175 implementation = DomImplementation 176 name = None 177 178 def testSerializer(element): 179 element.normalize() 180 rv = [] 181 182 def serializeElement(element, indent=0): 183 if element.nodeType == Node.DOCUMENT_TYPE_NODE: 184 if element.name: 185 if element.publicId or element.systemId: 186 publicId = element.publicId or "" 187 systemId = element.systemId or "" 188 rv.append("""|%s<!DOCTYPE %s "%s" "%s">""" % 189 (' ' * indent, element.name, publicId, systemId)) 190 else: 191 rv.append("|%s<!DOCTYPE %s>" % (' ' * indent, element.name)) 192 else: 193 rv.append("|%s<!DOCTYPE >" % (' ' * indent,)) 194 elif element.nodeType == Node.DOCUMENT_NODE: 195 rv.append("#document") 196 elif element.nodeType == Node.DOCUMENT_FRAGMENT_NODE: 197 rv.append("#document-fragment") 198 elif element.nodeType == Node.COMMENT_NODE: 199 rv.append("|%s<!-- %s -->" % (' ' * indent, element.nodeValue)) 200 elif element.nodeType == Node.TEXT_NODE: 201 rv.append("|%s\"%s\"" % (' ' * indent, element.nodeValue)) 202 else: 203 if (hasattr(element, "namespaceURI") and 204 element.namespaceURI is not None): 205 name = "%s %s" % (constants.prefixes[element.namespaceURI], 206 element.nodeName) 207 else: 208 name = element.nodeName 209 rv.append("|%s<%s>" % (' ' * indent, name)) 210 if element.hasAttributes(): 211 attributes = [] 212 for i in range(len(element.attributes)): 213 attr = element.attributes.item(i) 214 name = attr.nodeName 215 value = attr.value 216 ns = attr.namespaceURI 217 if ns: 218 name = "%s %s" % (constants.prefixes[ns], attr.localName) 219 else: 220 name = attr.nodeName 221 attributes.append((name, value)) 222 223 for name, value in sorted(attributes): 224 rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) 225 indent += 2 226 for child in element.childNodes: 227 serializeElement(child, indent) 228 serializeElement(element, 0) 229 230 return "\n".join(rv) 231 232 return locals() 233 234 235# The actual means to get a module! 236getDomModule = moduleFactoryFactory(getDomBuilder) 237