1 # -*- test-case-name: twisted.web.test.test_domhelpers -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 A library for performing interesting tasks with DOM objects.
11 from twisted.web import microdom
12 from twisted.web.microdom import getElementsByTagName, escape, unescape
15 class NodeLookupError(Exception):
19 def substitute(request, node, subs):
21 Look through the given node's children for strings, and
22 attempt to do string substitution with the given parameter.
24 for child in node.childNodes:
25 if hasattr(child, 'nodeValue') and child.nodeValue:
26 child.replaceData(0, len(child.nodeValue), child.nodeValue % subs)
27 substitute(request, child, subs)
29 def _get(node, nodeId, nodeAttrs=('id','class','model','pattern')):
31 (internal) Get a node with the specified C{nodeId} as any of the C{class},
32 C{id} or C{pattern} attributes.
35 if hasattr(node, 'hasAttributes') and node.hasAttributes():
36 for nodeAttr in nodeAttrs:
37 if (str (node.getAttribute(nodeAttr)) == nodeId):
39 if node.hasChildNodes():
40 if hasattr(node.childNodes, 'length'):
41 length = node.childNodes.length
43 length = len(node.childNodes)
44 for childNum in range(length):
45 result = _get(node.childNodes[childNum], nodeId)
46 if result: return result
48 def get(node, nodeId):
50 Get a node with the specified C{nodeId} as any of the C{class},
51 C{id} or C{pattern} attributes. If there is no such node, raise
54 result = _get(node, nodeId)
55 if result: return result
56 raise NodeLookupError, nodeId
58 def getIfExists(node, nodeId):
60 Get a node with the specified C{nodeId} as any of the C{class},
61 C{id} or C{pattern} attributes. If there is no such node, return
64 return _get(node, nodeId)
66 def getAndClear(node, nodeId):
67 """Get a node with the specified C{nodeId} as any of the C{class},
68 C{id} or C{pattern} attributes. If there is no such node, raise
69 L{NodeLookupError}. Remove all child nodes before returning.
71 result = get(node, nodeId)
78 Remove all children from the given node.
80 node.childNodes[:] = []
82 def locateNodes(nodeList, key, value, noNesting=1):
84 Find subnodes in the given node where the given attribute
88 if not isinstance(nodeList, type([])):
89 return locateNodes(nodeList.childNodes, key, value, noNesting)
90 for childNode in nodeList:
91 if not hasattr(childNode, 'getAttribute'):
93 if str(childNode.getAttribute(key)) == value:
94 returnList.append(childNode)
97 returnList.extend(locateNodes(childNode, key, value, noNesting))
100 def superSetAttribute(node, key, value):
101 if not hasattr(node, 'setAttribute'): return
102 node.setAttribute(key, value)
103 if node.hasChildNodes():
104 for child in node.childNodes:
105 superSetAttribute(child, key, value)
107 def superPrependAttribute(node, key, value):
108 if not hasattr(node, 'setAttribute'): return
109 old = node.getAttribute(key)
111 node.setAttribute(key, value+'/'+old)
113 node.setAttribute(key, value)
114 if node.hasChildNodes():
115 for child in node.childNodes:
116 superPrependAttribute(child, key, value)
118 def superAppendAttribute(node, key, value):
119 if not hasattr(node, 'setAttribute'): return
120 old = node.getAttribute(key)
122 node.setAttribute(key, old + '/' + value)
124 node.setAttribute(key, value)
125 if node.hasChildNodes():
126 for child in node.childNodes:
127 superAppendAttribute(child, key, value)
129 def gatherTextNodes(iNode, dounescape=0, joinWith=""):
130 """Visit each child node and collect its text data, if any, into a string.
132 >>> doc=microdom.parseString('<a>1<b>2<c>3</c>4</b></a>')
133 >>> gatherTextNodes(doc.documentElement)
135 With dounescape=1, also convert entities back into normal characters.
136 @return: the gathered nodes as a single string
140 gathered_append=gathered.append
144 if hasattr(c, 'nodeValue') and c.nodeValue is not None:
146 val=unescape(c.nodeValue)
150 slice[:0]=c.childNodes
151 return joinWith.join(gathered)
153 class RawText(microdom.Text):
154 """This is an evil and horrible speed hack. Basically, if you have a big
155 chunk of XML that you want to insert into the DOM, but you don't want to
156 incur the cost of parsing it, you can construct one of these and insert it
157 into the DOM. This will most certainly only work with microdom as the API
158 for converting nodes to xml is different in every DOM implementation.
160 This could be improved by making this class a Lazy parser, so if you
161 inserted this into the DOM and then later actually tried to mutate this
162 node, it would be parsed then.
165 def writexml(self, writer, indent="", addindent="", newl="", strip=0, nsprefixes=None, namespace=None):
166 writer.write("%s%s%s" % (indent, self.data, newl))
168 def findNodes(parent, matcher, accum=None):
171 if not parent.hasChildNodes():
173 for child in parent.childNodes:
174 # print child, child.nodeType, child.nodeName
177 findNodes(child, matcher, accum)
181 def findNodesShallowOnMatch(parent, matcher, recurseMatcher, accum=None):
184 if not parent.hasChildNodes():
186 for child in parent.childNodes:
187 # print child, child.nodeType, child.nodeName
190 if recurseMatcher(child):
191 findNodesShallowOnMatch(child, matcher, recurseMatcher, accum)
194 def findNodesShallow(parent, matcher, accum=None):
197 if not parent.hasChildNodes():
199 for child in parent.childNodes:
203 findNodes(child, matcher, accum)
207 def findElementsWithAttributeShallow(parent, attribute):
209 Return an iterable of the elements which are direct children of C{parent}
210 and which have the C{attribute} attribute.
212 return findNodesShallow(parent,
213 lambda n: getattr(n, 'tagName', None) is not None and
214 n.hasAttribute(attribute))
217 def findElements(parent, matcher):
219 Return an iterable of the elements which are children of C{parent} for
220 which the predicate C{matcher} returns true.
224 lambda n, matcher=matcher: getattr(n, 'tagName', None) is not None and
227 def findElementsWithAttribute(parent, attribute, value=None):
231 lambda n, attribute=attribute, value=value:
232 n.hasAttribute(attribute) and n.getAttribute(attribute) == value)
236 lambda n, attribute=attribute: n.hasAttribute(attribute))
239 def findNodesNamed(parent, name):
240 return findNodes(parent, lambda n, name=name: n.nodeName == name)
243 def writeNodeData(node, oldio):
244 for subnode in node.childNodes:
245 if hasattr(subnode, 'data'):
246 oldio.write(subnode.data)
248 writeNodeData(subnode, oldio)
251 def getNodeText(node):
252 oldio = StringIO.StringIO()
253 writeNodeData(node, oldio)
254 return oldio.getvalue()
257 def getParents(node):
261 node = node.parentNode
264 def namedChildren(parent, nodeName):
265 """namedChildren(parent, nodeName) -> children (not descendants) of parent
266 that have tagName == nodeName
268 return [n for n in parent.childNodes if getattr(n, 'tagName', '')==nodeName]