1 # -*- test-case-name: twisted.words.test.test_xpath -*-
3 # Copyright (c) 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)