/vendor/packages/twisted/twisted/words/xish/xpath.py

https://github.com/openhatch/oh-mainline
Python | 333 lines | 269 code | 40 blank | 24 comment | 24 complexity | 3dcf919dca2a13e7fef4a7062735ff7e MD5 | raw file
  1. # -*- test-case-name: twisted.words.test.test_xpath -*-
  2. #
  3. # Copyright (c) Twisted Matrix Laboratories.
  4. # See LICENSE for details.
  5. """
  6. XPath query support.
  7. This module provides L{XPathQuery} to match
  8. L{domish.Element<twisted.words.xish.domish.Element>} instances against
  9. XPath-like expressions.
  10. """
  11. try:
  12. import cStringIO as StringIO
  13. except ImportError:
  14. import StringIO
  15. class LiteralValue(str):
  16. def value(self, elem):
  17. return self
  18. class IndexValue:
  19. def __init__(self, index):
  20. self.index = int(index) - 1
  21. def value(self, elem):
  22. return elem.children[self.index]
  23. class AttribValue:
  24. def __init__(self, attribname):
  25. self.attribname = attribname
  26. if self.attribname == "xmlns":
  27. self.value = self.value_ns
  28. def value_ns(self, elem):
  29. return elem.uri
  30. def value(self, elem):
  31. if self.attribname in elem.attributes:
  32. return elem.attributes[self.attribname]
  33. else:
  34. return None
  35. class CompareValue:
  36. def __init__(self, lhs, op, rhs):
  37. self.lhs = lhs
  38. self.rhs = rhs
  39. if op == "=":
  40. self.value = self._compareEqual
  41. else:
  42. self.value = self._compareNotEqual
  43. def _compareEqual(self, elem):
  44. return self.lhs.value(elem) == self.rhs.value(elem)
  45. def _compareNotEqual(self, elem):
  46. return self.lhs.value(elem) != self.rhs.value(elem)
  47. class BooleanValue:
  48. """
  49. Provide boolean XPath expression operators.
  50. @ivar lhs: Left hand side expression of the operator.
  51. @ivar op: The operator. One of C{'and'}, C{'or'}.
  52. @ivar rhs: Right hand side expression of the operator.
  53. @ivar value: Reference to the method that will calculate the value of
  54. this expression given an element.
  55. """
  56. def __init__(self, lhs, op, rhs):
  57. self.lhs = lhs
  58. self.rhs = rhs
  59. if op == "and":
  60. self.value = self._booleanAnd
  61. else:
  62. self.value = self._booleanOr
  63. def _booleanAnd(self, elem):
  64. """
  65. Calculate boolean and of the given expressions given an element.
  66. @param elem: The element to calculate the value of the expression from.
  67. """
  68. return self.lhs.value(elem) and self.rhs.value(elem)
  69. def _booleanOr(self, elem):
  70. """
  71. Calculate boolean or of the given expressions given an element.
  72. @param elem: The element to calculate the value of the expression from.
  73. """
  74. return self.lhs.value(elem) or self.rhs.value(elem)
  75. def Function(fname):
  76. """
  77. Internal method which selects the function object
  78. """
  79. klassname = "_%s_Function" % fname
  80. c = globals()[klassname]()
  81. return c
  82. class _not_Function:
  83. def __init__(self):
  84. self.baseValue = None
  85. def setParams(self, baseValue):
  86. self.baseValue = baseValue
  87. def value(self, elem):
  88. return not self.baseValue.value(elem)
  89. class _text_Function:
  90. def setParams(self):
  91. pass
  92. def value(self, elem):
  93. return str(elem)
  94. class _Location:
  95. def __init__(self):
  96. self.predicates = []
  97. self.elementName = None
  98. self.childLocation = None
  99. def matchesPredicates(self, elem):
  100. if self.elementName != None and self.elementName != elem.name:
  101. return 0
  102. for p in self.predicates:
  103. if not p.value(elem):
  104. return 0
  105. return 1
  106. def matches(self, elem):
  107. if not self.matchesPredicates(elem):
  108. return 0
  109. if self.childLocation != None:
  110. for c in elem.elements():
  111. if self.childLocation.matches(c):
  112. return 1
  113. else:
  114. return 1
  115. return 0
  116. def queryForString(self, elem, resultbuf):
  117. if not self.matchesPredicates(elem):
  118. return
  119. if self.childLocation != None:
  120. for c in elem.elements():
  121. self.childLocation.queryForString(c, resultbuf)
  122. else:
  123. resultbuf.write(str(elem))
  124. def queryForNodes(self, elem, resultlist):
  125. if not self.matchesPredicates(elem):
  126. return
  127. if self.childLocation != None:
  128. for c in elem.elements():
  129. self.childLocation.queryForNodes(c, resultlist)
  130. else:
  131. resultlist.append(elem)
  132. def queryForStringList(self, elem, resultlist):
  133. if not self.matchesPredicates(elem):
  134. return
  135. if self.childLocation != None:
  136. for c in elem.elements():
  137. self.childLocation.queryForStringList(c, resultlist)
  138. else:
  139. for c in elem.children:
  140. if isinstance(c, (str, unicode)):
  141. resultlist.append(c)
  142. class _AnyLocation:
  143. def __init__(self):
  144. self.predicates = []
  145. self.elementName = None
  146. self.childLocation = None
  147. def matchesPredicates(self, elem):
  148. for p in self.predicates:
  149. if not p.value(elem):
  150. return 0
  151. return 1
  152. def listParents(self, elem, parentlist):
  153. if elem.parent != None:
  154. self.listParents(elem.parent, parentlist)
  155. parentlist.append(elem.name)
  156. def isRootMatch(self, elem):
  157. if (self.elementName == None or self.elementName == elem.name) and \
  158. self.matchesPredicates(elem):
  159. if self.childLocation != None:
  160. for c in elem.elements():
  161. if self.childLocation.matches(c):
  162. return True
  163. else:
  164. return True
  165. return False
  166. def findFirstRootMatch(self, elem):
  167. if (self.elementName == None or self.elementName == elem.name) and \
  168. self.matchesPredicates(elem):
  169. # Thus far, the name matches and the predicates match,
  170. # now check into the children and find the first one
  171. # that matches the rest of the structure
  172. # the rest of the structure
  173. if self.childLocation != None:
  174. for c in elem.elements():
  175. if self.childLocation.matches(c):
  176. return c
  177. return None
  178. else:
  179. # No children locations; this is a match!
  180. return elem
  181. else:
  182. # Ok, predicates or name didn't match, so we need to start
  183. # down each child and treat it as the root and try
  184. # again
  185. for c in elem.elements():
  186. if self.matches(c):
  187. return c
  188. # No children matched...
  189. return None
  190. def matches(self, elem):
  191. if self.isRootMatch(elem):
  192. return True
  193. else:
  194. # Ok, initial element isn't an exact match, walk
  195. # down each child and treat it as the root and try
  196. # again
  197. for c in elem.elements():
  198. if self.matches(c):
  199. return True
  200. # No children matched...
  201. return False
  202. def queryForString(self, elem, resultbuf):
  203. raise NotImplementedError(
  204. "queryForString is not implemented for any location")
  205. def queryForNodes(self, elem, resultlist):
  206. # First check to see if _this_ element is a root
  207. if self.isRootMatch(elem):
  208. resultlist.append(elem)
  209. # Now check each child
  210. for c in elem.elements():
  211. self.queryForNodes(c, resultlist)
  212. def queryForStringList(self, elem, resultlist):
  213. if self.isRootMatch(elem):
  214. for c in elem.children:
  215. if isinstance(c, (str, unicode)):
  216. resultlist.append(c)
  217. for c in elem.elements():
  218. self.queryForStringList(c, resultlist)
  219. class XPathQuery:
  220. def __init__(self, queryStr):
  221. self.queryStr = queryStr
  222. from twisted.words.xish.xpathparser import parse
  223. self.baseLocation = parse('XPATH', queryStr)
  224. def __hash__(self):
  225. return self.queryStr.__hash__()
  226. def matches(self, elem):
  227. return self.baseLocation.matches(elem)
  228. def queryForString(self, elem):
  229. result = StringIO.StringIO()
  230. self.baseLocation.queryForString(elem, result)
  231. return result.getvalue()
  232. def queryForNodes(self, elem):
  233. result = []
  234. self.baseLocation.queryForNodes(elem, result)
  235. if len(result) == 0:
  236. return None
  237. else:
  238. return result
  239. def queryForStringList(self, elem):
  240. result = []
  241. self.baseLocation.queryForStringList(elem, result)
  242. if len(result) == 0:
  243. return None
  244. else:
  245. return result
  246. __internedQueries = {}
  247. def internQuery(queryString):
  248. if queryString not in __internedQueries:
  249. __internedQueries[queryString] = XPathQuery(queryString)
  250. return __internedQueries[queryString]
  251. def matches(xpathstr, elem):
  252. return internQuery(xpathstr).matches(elem)
  253. def queryForStringList(xpathstr, elem):
  254. return internQuery(xpathstr).queryForStringList(elem)
  255. def queryForString(xpathstr, elem):
  256. return internQuery(xpathstr).queryForString(elem)
  257. def queryForNodes(xpathstr, elem):
  258. return internQuery(xpathstr).queryForNodes(elem)