aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/web/domhelpers.py
blob: 1ca491b470c970894bb5d1e375359501934fbea8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# -*- test-case-name: twisted.web.test.test_domhelpers -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
A library for performing interesting tasks with DOM objects.
"""

from io import StringIO

from twisted.web import microdom
from twisted.web.microdom import getElementsByTagName, escape, unescape
# These modules are imported here as a shortcut.
escape
getElementsByTagName



class NodeLookupError(Exception):
    pass


def substitute(request, node, subs):
    """
    Look through the given node's children for strings, and
    attempt to do string substitution with the given parameter.
    """
    for child in node.childNodes:
        if hasattr(child, 'nodeValue') and child.nodeValue:
            child.replaceData(0, len(child.nodeValue), child.nodeValue % subs)
        substitute(request, child, subs)

def _get(node, nodeId, nodeAttrs=('id','class','model','pattern')):
    """
    (internal) Get a node with the specified C{nodeId} as any of the C{class},
    C{id} or C{pattern} attributes.
    """

    if hasattr(node, 'hasAttributes') and node.hasAttributes():
        for nodeAttr in nodeAttrs:
            if (str (node.getAttribute(nodeAttr)) == nodeId):
                return node
    if node.hasChildNodes():
        if hasattr(node.childNodes, 'length'):
            length = node.childNodes.length
        else:
            length = len(node.childNodes)
        for childNum in range(length):
            result = _get(node.childNodes[childNum], nodeId)
            if result: return result

def get(node, nodeId):
    """
    Get a node with the specified C{nodeId} as any of the C{class},
    C{id} or C{pattern} attributes. If there is no such node, raise
    L{NodeLookupError}.
    """
    result = _get(node, nodeId)
    if result: return result
    raise NodeLookupError(nodeId)

def getIfExists(node, nodeId):
    """
    Get a node with the specified C{nodeId} as any of the C{class},
    C{id} or C{pattern} attributes.  If there is no such node, return
    L{None}.
    """
    return _get(node, nodeId)

def getAndClear(node, nodeId):
    """Get a node with the specified C{nodeId} as any of the C{class},
    C{id} or C{pattern} attributes. If there is no such node, raise
    L{NodeLookupError}. Remove all child nodes before returning.
    """
    result = get(node, nodeId)
    if result:
        clearNode(result)
    return result

def clearNode(node):
    """
    Remove all children from the given node.
    """
    node.childNodes[:] = []

def locateNodes(nodeList, key, value, noNesting=1):
    """
    Find subnodes in the given node where the given attribute
    has the given value.
    """
    returnList = []
    if not isinstance(nodeList, type([])):
        return locateNodes(nodeList.childNodes, key, value, noNesting)
    for childNode in nodeList:
        if not hasattr(childNode, 'getAttribute'):
            continue
        if str(childNode.getAttribute(key)) == value:
            returnList.append(childNode)
            if noNesting:
                continue
        returnList.extend(locateNodes(childNode, key, value, noNesting))
    return returnList

def superSetAttribute(node, key, value):
    if not hasattr(node, 'setAttribute'): return
    node.setAttribute(key, value)
    if node.hasChildNodes():
        for child in node.childNodes:
            superSetAttribute(child, key, value)

def superPrependAttribute(node, key, value):
    if not hasattr(node, 'setAttribute'): return
    old = node.getAttribute(key)
    if old:
        node.setAttribute(key, value+'/'+old)
    else:
        node.setAttribute(key, value)
    if node.hasChildNodes():
        for child in node.childNodes:
            superPrependAttribute(child, key, value)

def superAppendAttribute(node, key, value):
    if not hasattr(node, 'setAttribute'): return
    old = node.getAttribute(key)
    if old:
        node.setAttribute(key, old + '/' + value)
    else:
        node.setAttribute(key, value)
    if node.hasChildNodes():
        for child in node.childNodes:
            superAppendAttribute(child, key, value)

def gatherTextNodes(iNode, dounescape=0, joinWith=""):
    """Visit each child node and collect its text data, if any, into a string.
For example::
    >>> doc=microdom.parseString('<a>1<b>2<c>3</c>4</b></a>')
    >>> gatherTextNodes(doc.documentElement)
    '1234'
With dounescape=1, also convert entities back into normal characters.
@return: the gathered nodes as a single string
@rtype: str
"""
    gathered=[]
    gathered_append=gathered.append
    slice=[iNode]
    while len(slice)>0:
        c=slice.pop(0)
        if hasattr(c, 'nodeValue') and c.nodeValue is not None:
            if dounescape:
                val=unescape(c.nodeValue)
            else:
                val=c.nodeValue
            gathered_append(val)
        slice[:0]=c.childNodes
    return joinWith.join(gathered)

class RawText(microdom.Text):
    """This is an evil and horrible speed hack. Basically, if you have a big
    chunk of XML that you want to insert into the DOM, but you don't want to
    incur the cost of parsing it, you can construct one of these and insert it
    into the DOM. This will most certainly only work with microdom as the API
    for converting nodes to xml is different in every DOM implementation.

    This could be improved by making this class a Lazy parser, so if you
    inserted this into the DOM and then later actually tried to mutate this
    node, it would be parsed then.
    """

    def writexml(self, writer, indent="", addindent="", newl="", strip=0, nsprefixes=None, namespace=None):
        writer.write("%s%s%s" % (indent, self.data, newl))

def findNodes(parent, matcher, accum=None):
    if accum is None:
        accum = []
    if not parent.hasChildNodes():
        return accum
    for child in parent.childNodes:
        # print child, child.nodeType, child.nodeName
        if matcher(child):
            accum.append(child)
        findNodes(child, matcher, accum)
    return accum


def findNodesShallowOnMatch(parent, matcher, recurseMatcher, accum=None):
    if accum is None:
        accum = []
    if not parent.hasChildNodes():
        return accum
    for child in parent.childNodes:
        # print child, child.nodeType, child.nodeName
        if matcher(child):
            accum.append(child)
        if recurseMatcher(child):
            findNodesShallowOnMatch(child, matcher, recurseMatcher, accum)
    return accum

def findNodesShallow(parent, matcher, accum=None):
    if accum is None:
        accum = []
    if not parent.hasChildNodes():
        return accum
    for child in parent.childNodes:
        if matcher(child):
            accum.append(child)
        else:
            findNodes(child, matcher, accum)
    return accum


def findElementsWithAttributeShallow(parent, attribute):
    """
    Return an iterable of the elements which are direct children of C{parent}
    and which have the C{attribute} attribute.
    """
    return findNodesShallow(parent,
        lambda n: getattr(n, 'tagName', None) is not None and
            n.hasAttribute(attribute))


def findElements(parent, matcher):
    """
    Return an iterable of the elements which are children of C{parent} for
    which the predicate C{matcher} returns true.
    """
    return findNodes(
        parent,
        lambda n, matcher=matcher: getattr(n, 'tagName', None) is not None and
                                   matcher(n))

def findElementsWithAttribute(parent, attribute, value=None):
    if value:
        return findElements(
            parent,
            lambda n, attribute=attribute, value=value:
              n.hasAttribute(attribute) and n.getAttribute(attribute) == value)
    else:
        return findElements(
            parent,
            lambda n, attribute=attribute: n.hasAttribute(attribute))


def findNodesNamed(parent, name):
    return findNodes(parent, lambda n, name=name: n.nodeName == name)


def writeNodeData(node, oldio):
    for subnode in node.childNodes:
        if hasattr(subnode, 'data'):
            oldio.write(u"" + subnode.data)
        else:
            writeNodeData(subnode, oldio)


def getNodeText(node):
    oldio = StringIO()
    writeNodeData(node, oldio)
    return oldio.getvalue()


def getParents(node):
    l = []
    while node:
        l.append(node)
        node = node.parentNode
    return l

def namedChildren(parent, nodeName):
    """namedChildren(parent, nodeName) -> children (not descendants) of parent
    that have tagName == nodeName
    """
    return [n for n in parent.childNodes if getattr(n, 'tagName', '')==nodeName]