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