/tools/Ruby/lib/ruby/1.8/rexml/xpath_parser.rb

http://github.com/agross/netopenspace · Ruby · 792 lines · 723 code · 16 blank · 53 comment · 38 complexity · a61e48eb468b67d28b01382d8eb55556 MD5 · raw file

  1. require 'rexml/namespace'
  2. require 'rexml/xmltokens'
  3. require 'rexml/attribute'
  4. require 'rexml/syncenumerator'
  5. require 'rexml/parsers/xpathparser'
  6. class Object
  7. def dclone
  8. clone
  9. end
  10. end
  11. class Symbol
  12. def dclone ; self ; end
  13. end
  14. class Fixnum
  15. def dclone ; self ; end
  16. end
  17. class Float
  18. def dclone ; self ; end
  19. end
  20. class Array
  21. def dclone
  22. klone = self.clone
  23. klone.clear
  24. self.each{|v| klone << v.dclone}
  25. klone
  26. end
  27. end
  28. module REXML
  29. # You don't want to use this class. Really. Use XPath, which is a wrapper
  30. # for this class. Believe me. You don't want to poke around in here.
  31. # There is strange, dark magic at work in this code. Beware. Go back! Go
  32. # back while you still can!
  33. class XPathParser
  34. include XMLTokens
  35. LITERAL = /^'([^']*)'|^"([^"]*)"/u
  36. def initialize( )
  37. @parser = REXML::Parsers::XPathParser.new
  38. @namespaces = nil
  39. @variables = {}
  40. end
  41. def namespaces=( namespaces={} )
  42. Functions::namespace_context = namespaces
  43. @namespaces = namespaces
  44. end
  45. def variables=( vars={} )
  46. Functions::variables = vars
  47. @variables = vars
  48. end
  49. def parse path, nodeset
  50. #puts "#"*40
  51. path_stack = @parser.parse( path )
  52. #puts "PARSE: #{path} => #{path_stack.inspect}"
  53. #puts "PARSE: nodeset = #{nodeset.inspect}"
  54. match( path_stack, nodeset )
  55. end
  56. def get_first path, nodeset
  57. #puts "#"*40
  58. path_stack = @parser.parse( path )
  59. #puts "PARSE: #{path} => #{path_stack.inspect}"
  60. #puts "PARSE: nodeset = #{nodeset.inspect}"
  61. first( path_stack, nodeset )
  62. end
  63. def predicate path, nodeset
  64. path_stack = @parser.parse( path )
  65. expr( path_stack, nodeset )
  66. end
  67. def []=( variable_name, value )
  68. @variables[ variable_name ] = value
  69. end
  70. # Performs a depth-first (document order) XPath search, and returns the
  71. # first match. This is the fastest, lightest way to return a single result.
  72. #
  73. # FIXME: This method is incomplete!
  74. def first( path_stack, node )
  75. #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
  76. return nil if path.size == 0
  77. case path[0]
  78. when :document
  79. # do nothing
  80. return first( path[1..-1], node )
  81. when :child
  82. for c in node.children
  83. #puts "#{depth}) CHILD checking #{name(c)}"
  84. r = first( path[1..-1], c )
  85. #puts "#{depth}) RETURNING #{r.inspect}" if r
  86. return r if r
  87. end
  88. when :qname
  89. name = path[2]
  90. #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
  91. if node.name == name
  92. #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
  93. return node if path.size == 3
  94. return first( path[3..-1], node )
  95. else
  96. return nil
  97. end
  98. when :descendant_or_self
  99. r = first( path[1..-1], node )
  100. return r if r
  101. for c in node.children
  102. r = first( path, c )
  103. return r if r
  104. end
  105. when :node
  106. return first( path[1..-1], node )
  107. when :any
  108. return first( path[1..-1], node )
  109. end
  110. return nil
  111. end
  112. def match( path_stack, nodeset )
  113. #puts "MATCH: path_stack = #{path_stack.inspect}"
  114. #puts "MATCH: nodeset = #{nodeset.inspect}"
  115. r = expr( path_stack, nodeset )
  116. #puts "MAIN EXPR => #{r.inspect}"
  117. r
  118. end
  119. private
  120. # Returns a String namespace for a node, given a prefix
  121. # The rules are:
  122. #
  123. # 1. Use the supplied namespace mapping first.
  124. # 2. If no mapping was supplied, use the context node to look up the namespace
  125. def get_namespace( node, prefix )
  126. if @namespaces
  127. return @namespaces[prefix] || ''
  128. else
  129. return node.namespace( prefix ) if node.node_type == :element
  130. return ''
  131. end
  132. end
  133. # Expr takes a stack of path elements and a set of nodes (either a Parent
  134. # or an Array and returns an Array of matching nodes
  135. ALL = [ :attribute, :element, :text, :processing_instruction, :comment ]
  136. ELEMENTS = [ :element ]
  137. def expr( path_stack, nodeset, context=nil )
  138. #puts "#"*15
  139. #puts "In expr with #{path_stack.inspect}"
  140. #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
  141. node_types = ELEMENTS
  142. return nodeset if path_stack.length == 0 || nodeset.length == 0
  143. while path_stack.length > 0
  144. #puts "#"*5
  145. #puts "Path stack = #{path_stack.inspect}"
  146. #puts "Nodeset is #{nodeset.inspect}"
  147. if nodeset.length == 0
  148. path_stack.clear
  149. return []
  150. end
  151. case (op = path_stack.shift)
  152. when :document
  153. nodeset = [ nodeset[0].root_node ]
  154. #puts ":document, nodeset = #{nodeset.inspect}"
  155. when :qname
  156. #puts "IN QNAME"
  157. prefix = path_stack.shift
  158. name = path_stack.shift
  159. nodeset.delete_if do |node|
  160. # FIXME: This DOUBLES the time XPath searches take
  161. ns = get_namespace( node, prefix )
  162. #puts "NS = #{ns.inspect}"
  163. #puts "node.node_type == :element => #{node.node_type == :element}"
  164. if node.node_type == :element
  165. #puts "node.name == #{name} => #{node.name == name}"
  166. if node.name == name
  167. #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
  168. end
  169. end
  170. !(node.node_type == :element and
  171. node.name == name and
  172. node.namespace == ns )
  173. end
  174. node_types = ELEMENTS
  175. when :any
  176. #puts "ANY 1: nodeset = #{nodeset.inspect}"
  177. #puts "ANY 1: node_types = #{node_types.inspect}"
  178. nodeset.delete_if { |node| !node_types.include?(node.node_type) }
  179. #puts "ANY 2: nodeset = #{nodeset.inspect}"
  180. when :self
  181. # This space left intentionally blank
  182. when :processing_instruction
  183. target = path_stack.shift
  184. nodeset.delete_if do |node|
  185. (node.node_type != :processing_instruction) or
  186. ( target!='' and ( node.target != target ) )
  187. end
  188. when :text
  189. nodeset.delete_if { |node| node.node_type != :text }
  190. when :comment
  191. nodeset.delete_if { |node| node.node_type != :comment }
  192. when :node
  193. # This space left intentionally blank
  194. node_types = ALL
  195. when :child
  196. new_nodeset = []
  197. nt = nil
  198. for node in nodeset
  199. nt = node.node_type
  200. new_nodeset += node.children if nt == :element or nt == :document
  201. end
  202. nodeset = new_nodeset
  203. node_types = ELEMENTS
  204. when :literal
  205. return path_stack.shift
  206. when :attribute
  207. new_nodeset = []
  208. case path_stack.shift
  209. when :qname
  210. prefix = path_stack.shift
  211. name = path_stack.shift
  212. for element in nodeset
  213. if element.node_type == :element
  214. #puts "Element name = #{element.name}"
  215. #puts "get_namespace( #{element.inspect}, #{prefix} ) = #{get_namespace(element, prefix)}"
  216. attrib = element.attribute( name, get_namespace(element, prefix) )
  217. #puts "attrib = #{attrib.inspect}"
  218. new_nodeset << attrib if attrib
  219. end
  220. end
  221. when :any
  222. #puts "ANY"
  223. for element in nodeset
  224. if element.node_type == :element
  225. new_nodeset += element.attributes.to_a
  226. end
  227. end
  228. end
  229. nodeset = new_nodeset
  230. when :parent
  231. #puts "PARENT 1: nodeset = #{nodeset}"
  232. nodeset = nodeset.collect{|n| n.parent}.compact
  233. #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
  234. #puts "PARENT 2: nodeset = #{nodeset.inspect}"
  235. node_types = ELEMENTS
  236. when :ancestor
  237. new_nodeset = []
  238. for node in nodeset
  239. while node.parent
  240. node = node.parent
  241. new_nodeset << node unless new_nodeset.include? node
  242. end
  243. end
  244. nodeset = new_nodeset
  245. node_types = ELEMENTS
  246. when :ancestor_or_self
  247. new_nodeset = []
  248. for node in nodeset
  249. if node.node_type == :element
  250. new_nodeset << node
  251. while ( node.parent )
  252. node = node.parent
  253. new_nodeset << node unless new_nodeset.include? node
  254. end
  255. end
  256. end
  257. nodeset = new_nodeset
  258. node_types = ELEMENTS
  259. when :predicate
  260. new_nodeset = []
  261. subcontext = { :size => nodeset.size }
  262. pred = path_stack.shift
  263. nodeset.each_with_index { |node, index|
  264. subcontext[ :node ] = node
  265. #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
  266. subcontext[ :index ] = index+1
  267. pc = pred.dclone
  268. #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
  269. result = expr( pc, [node], subcontext )
  270. result = result[0] if result.kind_of? Array and result.length == 1
  271. #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
  272. if result.kind_of? Numeric
  273. #puts "Adding node #{node.inspect}" if result == (index+1)
  274. new_nodeset << node if result == (index+1)
  275. elsif result.instance_of? Array
  276. if result.size > 0 and result.inject(false) {|k,s| s or k}
  277. #puts "Adding node #{node.inspect}" if result.size > 0
  278. new_nodeset << node if result.size > 0
  279. end
  280. else
  281. #puts "Adding node #{node.inspect}" if result
  282. new_nodeset << node if result
  283. end
  284. }
  285. #puts "New nodeset = #{new_nodeset.inspect}"
  286. #puts "Path_stack = #{path_stack.inspect}"
  287. nodeset = new_nodeset
  288. =begin
  289. predicate = path_stack.shift
  290. ns = nodeset.clone
  291. result = expr( predicate, ns )
  292. #puts "Result = #{result.inspect} (#{result.class.name})"
  293. #puts "nodeset = #{nodeset.inspect}"
  294. if result.kind_of? Array
  295. nodeset = result.zip(ns).collect{|m,n| n if m}.compact
  296. else
  297. nodeset = result ? nodeset : []
  298. end
  299. #puts "Outgoing NS = #{nodeset.inspect}"
  300. =end
  301. when :descendant_or_self
  302. rv = descendant_or_self( path_stack, nodeset )
  303. path_stack.clear
  304. nodeset = rv
  305. node_types = ELEMENTS
  306. when :descendant
  307. results = []
  308. nt = nil
  309. for node in nodeset
  310. nt = node.node_type
  311. results += expr( path_stack.dclone.unshift( :descendant_or_self ),
  312. node.children ) if nt == :element or nt == :document
  313. end
  314. nodeset = results
  315. node_types = ELEMENTS
  316. when :following_sibling
  317. #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
  318. results = []
  319. nodeset.each do |node|
  320. next if node.parent.nil?
  321. all_siblings = node.parent.children
  322. current_index = all_siblings.index( node )
  323. following_siblings = all_siblings[ current_index+1 .. -1 ]
  324. results += expr( path_stack.dclone, following_siblings )
  325. end
  326. #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
  327. nodeset = results
  328. when :preceding_sibling
  329. results = []
  330. nodeset.each do |node|
  331. next if node.parent.nil?
  332. all_siblings = node.parent.children
  333. current_index = all_siblings.index( node )
  334. preceding_siblings = all_siblings[ 0, current_index ].reverse
  335. results += preceding_siblings
  336. end
  337. nodeset = results
  338. node_types = ELEMENTS
  339. when :preceding
  340. new_nodeset = []
  341. for node in nodeset
  342. new_nodeset += preceding( node )
  343. end
  344. #puts "NEW NODESET => #{new_nodeset.inspect}"
  345. nodeset = new_nodeset
  346. node_types = ELEMENTS
  347. when :following
  348. new_nodeset = []
  349. for node in nodeset
  350. new_nodeset += following( node )
  351. end
  352. nodeset = new_nodeset
  353. node_types = ELEMENTS
  354. when :namespace
  355. #puts "In :namespace"
  356. new_nodeset = []
  357. prefix = path_stack.shift
  358. for node in nodeset
  359. if (node.node_type == :element or node.node_type == :attribute)
  360. if @namespaces
  361. namespaces = @namespaces
  362. elsif (node.node_type == :element)
  363. namespaces = node.namespaces
  364. else
  365. namespaces = node.element.namesapces
  366. end
  367. #puts "Namespaces = #{namespaces.inspect}"
  368. #puts "Prefix = #{prefix.inspect}"
  369. #puts "Node.namespace = #{node.namespace}"
  370. if (node.namespace == namespaces[prefix])
  371. new_nodeset << node
  372. end
  373. end
  374. end
  375. nodeset = new_nodeset
  376. when :variable
  377. var_name = path_stack.shift
  378. return @variables[ var_name ]
  379. # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
  380. # TODO: Special case for :or and :and -- not evaluate the right
  381. # operand if the left alone determines result (i.e. is true for
  382. # :or and false for :and).
  383. when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
  384. left = expr( path_stack.shift, nodeset.dup, context )
  385. #puts "LEFT => #{left.inspect} (#{left.class.name})"
  386. right = expr( path_stack.shift, nodeset.dup, context )
  387. #puts "RIGHT => #{right.inspect} (#{right.class.name})"
  388. res = equality_relational_compare( left, op, right )
  389. #puts "RES => #{res.inspect}"
  390. return res
  391. when :and
  392. left = expr( path_stack.shift, nodeset.dup, context )
  393. #puts "LEFT => #{left.inspect} (#{left.class.name})"
  394. if left == false || left.nil? || !left.inject(false) {|a,b| a | b}
  395. return []
  396. end
  397. right = expr( path_stack.shift, nodeset.dup, context )
  398. #puts "RIGHT => #{right.inspect} (#{right.class.name})"
  399. res = equality_relational_compare( left, op, right )
  400. #puts "RES => #{res.inspect}"
  401. return res
  402. when :div
  403. left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
  404. right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
  405. return (left / right)
  406. when :mod
  407. left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  408. right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  409. return (left % right)
  410. when :mult
  411. left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  412. right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  413. return (left * right)
  414. when :plus
  415. left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  416. right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  417. return (left + right)
  418. when :minus
  419. left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  420. right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
  421. return (left - right)
  422. when :union
  423. left = expr( path_stack.shift, nodeset, context )
  424. right = expr( path_stack.shift, nodeset, context )
  425. return (left | right)
  426. when :neg
  427. res = expr( path_stack, nodeset, context )
  428. return -(res.to_f)
  429. when :not
  430. when :function
  431. func_name = path_stack.shift.tr('-','_')
  432. arguments = path_stack.shift
  433. #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})"
  434. subcontext = context ? nil : { :size => nodeset.size }
  435. res = []
  436. cont = context
  437. nodeset.each_with_index { |n, i|
  438. if subcontext
  439. subcontext[:node] = n
  440. subcontext[:index] = i
  441. cont = subcontext
  442. end
  443. arg_clone = arguments.dclone
  444. args = arg_clone.collect { |arg|
  445. #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
  446. expr( arg, [n], cont )
  447. }
  448. #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})"
  449. Functions.context = cont
  450. res << Functions.send( func_name, *args )
  451. #puts "FUNCTION 3: #{res[-1].inspect}"
  452. }
  453. return res
  454. end
  455. end # while
  456. #puts "EXPR returning #{nodeset.inspect}"
  457. return nodeset
  458. end
  459. ##########################################################
  460. # FIXME
  461. # The next two methods are BAD MOJO!
  462. # This is my achilles heel. If anybody thinks of a better
  463. # way of doing this, be my guest. This really sucks, but
  464. # it is a wonder it works at all.
  465. # ########################################################
  466. def descendant_or_self( path_stack, nodeset )
  467. rs = []
  468. #puts "#"*80
  469. #puts "PATH_STACK = #{path_stack.inspect}"
  470. #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}"
  471. d_o_s( path_stack, nodeset, rs )
  472. #puts "RS = #{rs.collect{|n|n.inspect}.inspect}"
  473. document_order(rs.flatten.compact)
  474. #rs.flatten.compact
  475. end
  476. def d_o_s( p, ns, r )
  477. #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
  478. nt = nil
  479. ns.each_index do |i|
  480. n = ns[i]
  481. #puts "P => #{p.inspect}"
  482. x = expr( p.dclone, [ n ] )
  483. nt = n.node_type
  484. d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
  485. r.concat(x) if x.size > 0
  486. end
  487. end
  488. # Reorders an array of nodes so that they are in document order
  489. # It tries to do this efficiently.
  490. #
  491. # FIXME: I need to get rid of this, but the issue is that most of the XPath
  492. # interpreter functions as a filter, which means that we lose context going
  493. # in and out of function calls. If I knew what the index of the nodes was,
  494. # I wouldn't have to do this. Maybe add a document IDX for each node?
  495. # Problems with mutable documents. Or, rewrite everything.
  496. def document_order( array_of_nodes )
  497. new_arry = []
  498. array_of_nodes.each { |node|
  499. node_idx = []
  500. np = node.node_type == :attribute ? node.element : node
  501. while np.parent and np.parent.node_type == :element
  502. node_idx << np.parent.index( np )
  503. np = np.parent
  504. end
  505. new_arry << [ node_idx.reverse, node ]
  506. }
  507. #puts "new_arry = #{new_arry.inspect}"
  508. new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
  509. end
  510. def recurse( nodeset, &block )
  511. for node in nodeset
  512. yield node
  513. recurse( node, &block ) if node.node_type == :element
  514. end
  515. end
  516. # Builds a nodeset of all of the preceding nodes of the supplied node,
  517. # in reverse document order
  518. # preceding:: includes every element in the document that precedes this node,
  519. # except for ancestors
  520. def preceding( node )
  521. #puts "IN PRECEDING"
  522. ancestors = []
  523. p = node.parent
  524. while p
  525. ancestors << p
  526. p = p.parent
  527. end
  528. acc = []
  529. p = preceding_node_of( node )
  530. #puts "P = #{p.inspect}"
  531. while p
  532. if ancestors.include? p
  533. ancestors.delete(p)
  534. else
  535. acc << p
  536. end
  537. p = preceding_node_of( p )
  538. #puts "P = #{p.inspect}"
  539. end
  540. acc
  541. end
  542. def preceding_node_of( node )
  543. #puts "NODE: #{node.inspect}"
  544. #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
  545. #puts "PARENT NODE: #{node.parent}"
  546. psn = node.previous_sibling_node
  547. if psn.nil?
  548. if node.parent.nil? or node.parent.class == Document
  549. return nil
  550. end
  551. return node.parent
  552. #psn = preceding_node_of( node.parent )
  553. end
  554. while psn and psn.kind_of? Element and psn.children.size > 0
  555. psn = psn.children[-1]
  556. end
  557. psn
  558. end
  559. def following( node )
  560. #puts "IN PRECEDING"
  561. acc = []
  562. p = next_sibling_node( node )
  563. #puts "P = #{p.inspect}"
  564. while p
  565. acc << p
  566. p = following_node_of( p )
  567. #puts "P = #{p.inspect}"
  568. end
  569. acc
  570. end
  571. def following_node_of( node )
  572. #puts "NODE: #{node.inspect}"
  573. #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
  574. #puts "PARENT NODE: #{node.parent}"
  575. if node.kind_of? Element and node.children.size > 0
  576. return node.children[0]
  577. end
  578. return next_sibling_node(node)
  579. end
  580. def next_sibling_node(node)
  581. psn = node.next_sibling_node
  582. while psn.nil?
  583. if node.parent.nil? or node.parent.class == Document
  584. return nil
  585. end
  586. node = node.parent
  587. psn = node.next_sibling_node
  588. #puts "psn = #{psn.inspect}"
  589. end
  590. return psn
  591. end
  592. def norm b
  593. case b
  594. when true, false
  595. return b
  596. when 'true', 'false'
  597. return Functions::boolean( b )
  598. when /^\d+(\.\d+)?$/
  599. return Functions::number( b )
  600. else
  601. return Functions::string( b )
  602. end
  603. end
  604. def equality_relational_compare( set1, op, set2 )
  605. #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
  606. if set1.kind_of? Array and set2.kind_of? Array
  607. #puts "#{set1.size} & #{set2.size}"
  608. if set1.size == 1 and set2.size == 1
  609. set1 = set1[0]
  610. set2 = set2[0]
  611. elsif set1.size == 0 or set2.size == 0
  612. nd = set1.size==0 ? set2 : set1
  613. rv = nd.collect { |il| compare( il, op, nil ) }
  614. #puts "RV = #{rv.inspect}"
  615. return rv
  616. else
  617. res = []
  618. enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
  619. #puts "i1 = #{i1.inspect} (#{i1.class.name})"
  620. #puts "i2 = #{i2.inspect} (#{i2.class.name})"
  621. i1 = norm( i1 )
  622. i2 = norm( i2 )
  623. res << compare( i1, op, i2 )
  624. }
  625. return res
  626. end
  627. end
  628. #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
  629. #puts "COMPARING VALUES"
  630. # If one is nodeset and other is number, compare number to each item
  631. # in nodeset s.t. number op number(string(item))
  632. # If one is nodeset and other is string, compare string to each item
  633. # in nodeset s.t. string op string(item)
  634. # If one is nodeset and other is boolean, compare boolean to each item
  635. # in nodeset s.t. boolean op boolean(item)
  636. if set1.kind_of? Array or set2.kind_of? Array
  637. #puts "ISA ARRAY"
  638. if set1.kind_of? Array
  639. a = set1
  640. b = set2
  641. else
  642. a = set2
  643. b = set1
  644. end
  645. case b
  646. when true, false
  647. return a.collect {|v| compare( Functions::boolean(v), op, b ) }
  648. when Numeric
  649. return a.collect {|v| compare( Functions::number(v), op, b )}
  650. when /^\d+(\.\d+)?$/
  651. b = Functions::number( b )
  652. #puts "B = #{b.inspect}"
  653. return a.collect {|v| compare( Functions::number(v), op, b )}
  654. else
  655. #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
  656. b = Functions::string( b )
  657. return a.collect { |v| compare( Functions::string(v), op, b ) }
  658. end
  659. else
  660. # If neither is nodeset,
  661. # If op is = or !=
  662. # If either boolean, convert to boolean
  663. # If either number, convert to number
  664. # Else, convert to string
  665. # Else
  666. # Convert both to numbers and compare
  667. s1 = set1.to_s
  668. s2 = set2.to_s
  669. #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
  670. if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
  671. #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
  672. #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
  673. set1 = Functions::boolean( set1 )
  674. set2 = Functions::boolean( set2 )
  675. else
  676. if op == :eq or op == :neq
  677. if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
  678. set1 = Functions::number( s1 )
  679. set2 = Functions::number( s2 )
  680. else
  681. set1 = Functions::string( set1 )
  682. set2 = Functions::string( set2 )
  683. end
  684. else
  685. set1 = Functions::number( set1 )
  686. set2 = Functions::number( set2 )
  687. end
  688. end
  689. #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
  690. #puts ">>> #{compare( set1, op, set2 )}"
  691. return compare( set1, op, set2 )
  692. end
  693. return false
  694. end
  695. def compare a, op, b
  696. #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
  697. case op
  698. when :eq
  699. a == b
  700. when :neq
  701. a != b
  702. when :lt
  703. a < b
  704. when :lteq
  705. a <= b
  706. when :gt
  707. a > b
  708. when :gteq
  709. a >= b
  710. when :and
  711. a and b
  712. when :or
  713. a or b
  714. else
  715. false
  716. end
  717. end
  718. end
  719. end