PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/jruby-1.1.6RC1/lib/ruby/1.8/rexml/quickpath.rb

https://bitbucket.org/nicksieger/advent-jruby
Ruby | 266 lines | 218 code | 15 blank | 33 comment | 27 complexity | 8177182db9da55183dc38f3beadf7ba2 MD5 | raw file
Possible License(s): CPL-1.0, AGPL-1.0, LGPL-2.1, JSON
  1. require 'rexml/functions'
  2. require 'rexml/xmltokens'
  3. module REXML
  4. class QuickPath
  5. include Functions
  6. include XMLTokens
  7. EMPTY_HASH = {}
  8. def QuickPath::first element, path, namespaces=EMPTY_HASH
  9. match(element, path, namespaces)[0]
  10. end
  11. def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
  12. path = "*" unless path
  13. match(element, path, namespaces).each( &block )
  14. end
  15. def QuickPath::match element, path, namespaces=EMPTY_HASH
  16. raise "nil is not a valid xpath" unless path
  17. results = nil
  18. Functions::namespace_context = namespaces
  19. case path
  20. when /^\/([^\/]|$)/u
  21. # match on root
  22. path = path[1..-1]
  23. return [element.root.parent] if path == ''
  24. results = filter([element.root], path)
  25. when /^[-\w]*::/u
  26. results = filter([element], path)
  27. when /^\*/u
  28. results = filter(element.to_a, path)
  29. when /^[\[!\w:]/u
  30. # match on child
  31. matches = []
  32. children = element.to_a
  33. results = filter(children, path)
  34. else
  35. results = filter([element], path)
  36. end
  37. return results
  38. end
  39. # Given an array of nodes it filters the array based on the path. The
  40. # result is that when this method returns, the array will contain elements
  41. # which match the path
  42. def QuickPath::filter elements, path
  43. return elements if path.nil? or path == '' or elements.size == 0
  44. case path
  45. when /^\/\//u # Descendant
  46. return axe( elements, "descendant-or-self", $' )
  47. when /^\/?\b(\w[-\w]*)\b::/u # Axe
  48. axe_name = $1
  49. rest = $'
  50. return axe( elements, $1, $' )
  51. when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
  52. rest = $'
  53. results = []
  54. elements.each do |element|
  55. results |= filter( element.to_a, rest )
  56. end
  57. return results
  58. when /^\/?(\w[-\w]*)\(/u # / Function
  59. return function( elements, $1, $' )
  60. when Namespace::NAMESPLIT # Element name
  61. name = $2
  62. ns = $1
  63. rest = $'
  64. elements.delete_if do |element|
  65. !(element.kind_of? Element and
  66. (element.expanded_name == name or
  67. (element.name == name and
  68. element.namespace == Functions.namespace_context[ns])))
  69. end
  70. return filter( elements, rest )
  71. when /^\/\[/u
  72. matches = []
  73. elements.each do |element|
  74. matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
  75. end
  76. return matches
  77. when /^\[/u # Predicate
  78. return predicate( elements, path )
  79. when /^\/?\.\.\./u # Ancestor
  80. return axe( elements, "ancestor", $' )
  81. when /^\/?\.\./u # Parent
  82. return filter( elements.collect{|e|e.parent}, $' )
  83. when /^\/?\./u # Self
  84. return filter( elements, $' )
  85. when /^\*/u # Any
  86. results = []
  87. elements.each do |element|
  88. results |= filter( [element], $' ) if element.kind_of? Element
  89. #if element.kind_of? Element
  90. # children = element.to_a
  91. # children.delete_if { |child| !child.kind_of?(Element) }
  92. # results |= filter( children, $' )
  93. #end
  94. end
  95. return results
  96. end
  97. return []
  98. end
  99. def QuickPath::axe( elements, axe_name, rest )
  100. matches = []
  101. matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
  102. case axe_name
  103. when /^descendant/u
  104. elements.each do |element|
  105. matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
  106. end
  107. when /^ancestor/u
  108. elements.each do |element|
  109. while element.parent
  110. matches << element.parent
  111. element = element.parent
  112. end
  113. end
  114. matches = filter( matches, rest )
  115. when "self"
  116. matches = filter( elements, rest )
  117. when "child"
  118. elements.each do |element|
  119. matches |= filter( element.to_a, rest ) if element.kind_of? Element
  120. end
  121. when "attribute"
  122. elements.each do |element|
  123. matches << element.attributes[ rest ] if element.kind_of? Element
  124. end
  125. when "parent"
  126. matches = filter(elements.collect{|element| element.parent}.uniq, rest)
  127. when "following-sibling"
  128. matches = filter(elements.collect{|element| element.next_sibling}.uniq,
  129. rest)
  130. when "previous-sibling"
  131. matches = filter(elements.collect{|element|
  132. element.previous_sibling}.uniq, rest )
  133. end
  134. return matches.uniq
  135. end
  136. # A predicate filters a node-set with respect to an axis to produce a
  137. # new node-set. For each node in the node-set to be filtered, the
  138. # PredicateExpr is evaluated with that node as the context node, with
  139. # the number of nodes in the node-set as the context size, and with the
  140. # proximity position of the node in the node-set with respect to the
  141. # axis as the context position; if PredicateExpr evaluates to true for
  142. # that node, the node is included in the new node-set; otherwise, it is
  143. # not included.
  144. #
  145. # A PredicateExpr is evaluated by evaluating the Expr and converting
  146. # the result to a boolean. If the result is a number, the result will
  147. # be converted to true if the number is equal to the context position
  148. # and will be converted to false otherwise; if the result is not a
  149. # number, then the result will be converted as if by a call to the
  150. # boolean function. Thus a location path para[3] is equivalent to
  151. # para[position()=3].
  152. def QuickPath::predicate( elements, path )
  153. ind = 1
  154. bcount = 1
  155. while bcount > 0
  156. bcount += 1 if path[ind] == ?[
  157. bcount -= 1 if path[ind] == ?]
  158. ind += 1
  159. end
  160. ind -= 1
  161. predicate = path[1..ind-1]
  162. rest = path[ind+1..-1]
  163. # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
  164. predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) {
  165. "#$1 #$2 #$3 and #$3 #$4 #$5"
  166. }
  167. # Let's do some Ruby trickery to avoid some work:
  168. predicate.gsub!( /&/u, "&&" )
  169. predicate.gsub!( /=/u, "==" )
  170. predicate.gsub!( /@(\w[-\w.]*)/u ) {
  171. "attribute(\"#$1\")"
  172. }
  173. predicate.gsub!( /\bmod\b/u, "%" )
  174. predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
  175. fname = $1
  176. fname.gsub( /-/u, "_" )
  177. }
  178. Functions.pair = [ 0, elements.size ]
  179. results = []
  180. elements.each do |element|
  181. Functions.pair[0] += 1
  182. Functions.node = element
  183. res = eval( predicate )
  184. case res
  185. when true
  186. results << element
  187. when Fixnum
  188. results << element if Functions.pair[0] == res
  189. when String
  190. results << element
  191. end
  192. end
  193. return filter( results, rest )
  194. end
  195. def QuickPath::attribute( name )
  196. return Functions.node.attributes[name] if Functions.node.kind_of? Element
  197. end
  198. def QuickPath::name()
  199. return Functions.node.name if Functions.node.kind_of? Element
  200. end
  201. def QuickPath::method_missing( id, *args )
  202. begin
  203. Functions.send( id.id2name, *args )
  204. rescue Exception
  205. raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
  206. end
  207. end
  208. def QuickPath::function( elements, fname, rest )
  209. args = parse_args( elements, rest )
  210. Functions.pair = [0, elements.size]
  211. results = []
  212. elements.each do |element|
  213. Functions.pair[0] += 1
  214. Functions.node = element
  215. res = Functions.send( fname, *args )
  216. case res
  217. when true
  218. results << element
  219. when Fixnum
  220. results << element if Functions.pair[0] == res
  221. end
  222. end
  223. return results
  224. end
  225. def QuickPath::parse_args( element, string )
  226. # /.*?(?:\)|,)/
  227. arguments = []
  228. buffer = ""
  229. while string and string != ""
  230. c = string[0]
  231. string.sub!(/^./u, "")
  232. case c
  233. when ?,
  234. # if depth = 1, then we start a new argument
  235. arguments << evaluate( buffer )
  236. #arguments << evaluate( string[0..count] )
  237. when ?(
  238. # start a new method call
  239. function( element, buffer, string )
  240. buffer = ""
  241. when ?)
  242. # close the method call and return arguments
  243. return arguments
  244. else
  245. buffer << c
  246. end
  247. end
  248. ""
  249. end
  250. end
  251. end