1
# -*- test-case-name: twisted.words.test.test_xpath -*-
3
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
4
# See LICENSE for details.
9
This module provides L{XPathQuery} to match
10
L{domish.Element<twisted.words.xish.domish.Element>} instances against
11
XPath-like expressions.
15
import cStringIO as StringIO
19
class LiteralValue(str):
20
def value(self, elem):
25
def __init__(self, index):
26
self.index = int(index) - 1
28
def value(self, elem):
29
return elem.children[self.index]
33
def __init__(self, attribname):
34
self.attribname = attribname
35
if self.attribname == "xmlns":
36
self.value = self.value_ns
38
def value_ns(self, elem):
41
def value(self, elem):
42
if self.attribname in elem.attributes:
43
return elem.attributes[self.attribname]
49
def __init__(self, lhs, op, rhs):
53
self.value = self._compareEqual
55
self.value = self._compareNotEqual
57
def _compareEqual(self, elem):
58
return self.lhs.value(elem) == self.rhs.value(elem)
60
def _compareNotEqual(self, elem):
61
return self.lhs.value(elem) != self.rhs.value(elem)
66
Provide boolean XPath expression operators.
68
@ivar lhs: Left hand side expression of the operator.
69
@ivar op: The operator. One of C{'and'}, C{'or'}.
70
@ivar rhs: Right hand side expression of the operator.
71
@ivar value: Reference to the method that will calculate the value of
72
this expression given an element.
74
def __init__(self, lhs, op, rhs):
78
self.value = self._booleanAnd
80
self.value = self._booleanOr
82
def _booleanAnd(self, elem):
84
Calculate boolean and of the given expressions given an element.
86
@param elem: The element to calculate the value of the expression from.
88
return self.lhs.value(elem) and self.rhs.value(elem)
90
def _booleanOr(self, elem):
92
Calculate boolean or of the given expressions given an element.
94
@param elem: The element to calculate the value of the expression from.
96
return self.lhs.value(elem) or self.rhs.value(elem)
101
Internal method which selects the function object
103
klassname = "_%s_Function" % fname
104
c = globals()[klassname]()
110
self.baseValue = None
112
def setParams(self, baseValue):
113
self.baseValue = baseValue
115
def value(self, elem):
116
return not self.baseValue.value(elem)
119
class _text_Function:
123
def value(self, elem):
130
self.elementName = None
131
self.childLocation = None
133
def matchesPredicates(self, elem):
134
if self.elementName != None and self.elementName != elem.name:
137
for p in self.predicates:
138
if not p.value(elem):
143
def matches(self, elem):
144
if not self.matchesPredicates(elem):
147
if self.childLocation != None:
148
for c in elem.elements():
149
if self.childLocation.matches(c):
156
def queryForString(self, elem, resultbuf):
157
if not self.matchesPredicates(elem):
160
if self.childLocation != None:
161
for c in elem.elements():
162
self.childLocation.queryForString(c, resultbuf)
164
resultbuf.write(str(elem))
166
def queryForNodes(self, elem, resultlist):
167
if not self.matchesPredicates(elem):
170
if self.childLocation != None:
171
for c in elem.elements():
172
self.childLocation.queryForNodes(c, resultlist)
174
resultlist.append(elem)
176
def queryForStringList(self, elem, resultlist):
177
if not self.matchesPredicates(elem):
180
if self.childLocation != None:
181
for c in elem.elements():
182
self.childLocation.queryForStringList(c, resultlist)
184
for c in elem.children:
185
if isinstance(c, (str, unicode)):
192
self.elementName = None
193
self.childLocation = None
195
def matchesPredicates(self, elem):
196
for p in self.predicates:
197
if not p.value(elem):
201
def listParents(self, elem, parentlist):
202
if elem.parent != None:
203
self.listParents(elem.parent, parentlist)
204
parentlist.append(elem.name)
206
def isRootMatch(self, elem):
207
if (self.elementName == None or self.elementName == elem.name) and \
208
self.matchesPredicates(elem):
209
if self.childLocation != None:
210
for c in elem.elements():
211
if self.childLocation.matches(c):
217
def findFirstRootMatch(self, elem):
218
if (self.elementName == None or self.elementName == elem.name) and \
219
self.matchesPredicates(elem):
220
# Thus far, the name matches and the predicates match,
221
# now check into the children and find the first one
222
# that matches the rest of the structure
223
# the rest of the structure
224
if self.childLocation != None:
225
for c in elem.elements():
226
if self.childLocation.matches(c):
230
# No children locations; this is a match!
233
# Ok, predicates or name didn't match, so we need to start
234
# down each child and treat it as the root and try
236
for c in elem.elements():
239
# No children matched...
242
def matches(self, elem):
243
if self.isRootMatch(elem):
246
# Ok, initial element isn't an exact match, walk
247
# down each child and treat it as the root and try
249
for c in elem.elements():
252
# No children matched...
255
def queryForString(self, elem, resultbuf):
256
raise NotImplementedError(
257
"queryForString is not implemented for any location")
259
def queryForNodes(self, elem, resultlist):
260
# First check to see if _this_ element is a root
261
if self.isRootMatch(elem):
262
resultlist.append(elem)
264
# Now check each child
265
for c in elem.elements():
266
self.queryForNodes(c, resultlist)
269
def queryForStringList(self, elem, resultlist):
270
if self.isRootMatch(elem):
271
for c in elem.children:
272
if isinstance(c, (str, unicode)):
274
for c in elem.elements():
275
self.queryForStringList(c, resultlist)
279
def __init__(self, queryStr):
280
self.queryStr = queryStr
281
from twisted.words.xish.xpathparser import parse
282
self.baseLocation = parse('XPATH', queryStr)
285
return self.queryStr.__hash__()
287
def matches(self, elem):
288
return self.baseLocation.matches(elem)
290
def queryForString(self, elem):
291
result = StringIO.StringIO()
292
self.baseLocation.queryForString(elem, result)
293
return result.getvalue()
295
def queryForNodes(self, elem):
297
self.baseLocation.queryForNodes(elem, result)
303
def queryForStringList(self, elem):
305
self.baseLocation.queryForStringList(elem, result)
312
__internedQueries = {}
314
def internQuery(queryString):
315
if queryString not in __internedQueries:
316
__internedQueries[queryString] = XPathQuery(queryString)
317
return __internedQueries[queryString]
320
def matches(xpathstr, elem):
321
return internQuery(xpathstr).matches(elem)
324
def queryForStringList(xpathstr, elem):
325
return internQuery(xpathstr).queryForStringList(elem)
328
def queryForString(xpathstr, elem):
329
return internQuery(xpathstr).queryForString(elem)
332
def queryForNodes(xpathstr, elem):
333
return internQuery(xpathstr).queryForNodes(elem)