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