Evaluate XPath predicates for every context node.

Instead of evaluating a predicate once for all context nodes, they should
instead be evaluated separately per context node.
This commit is contained in:
Yorick Peterse 2014-11-15 00:31:44 +01:00
parent 6daa3e7a00
commit f1d574f342
2 changed files with 35 additions and 28 deletions

View File

@ -188,30 +188,32 @@ module Oga
# #
def on_predicate(ast_node, context) def on_predicate(ast_node, context)
test, predicate = *ast_node test, predicate = *ast_node
final_nodes = XML::NodeSet.new
initial_nodes = process(test, context) context.each do |context_node|
final_nodes = XML::NodeSet.new initial_nodes = process(test, XML::NodeSet.new([context_node]))
xpath_index = 1 xpath_index = 1
initial_nodes.each do |xml_node| initial_nodes.each do |xml_node|
retval = with_node_set(initial_nodes) do retval = with_node_set(initial_nodes) do
process(predicate, XML::NodeSet.new([xml_node])) process(predicate, XML::NodeSet.new([xml_node]))
end
# Numeric values are used as node set indexes.
if retval.is_a?(Numeric)
final_nodes << xml_node if retval.to_i == xpath_index
# Node sets, strings, booleans, etc
elsif retval
if retval.respond_to?(:empty?) and retval.empty?
next
end end
final_nodes << xml_node # Numeric values are used as node set indexes.
end if retval.is_a?(Numeric)
final_nodes << xml_node if retval.to_i == xpath_index
xpath_index += 1 # Node sets, strings, booleans, etc
elsif retval
if retval.respond_to?(:empty?) and retval.empty?
next
end
final_nodes << xml_node
end
xpath_index += 1
end
end end
return final_nodes return final_nodes

View File

@ -3,18 +3,23 @@ require 'spec_helper'
describe Oga::XPath::Evaluator do describe Oga::XPath::Evaluator do
context 'predicates' do context 'predicates' do
before do before do
@document = parse('<root><b>10</b><b>20</b></root>') @document = parse(<<-EOF)
<root>
<a>10</a>
<b>
<a>20</a>
<a>30</3>
</b>
</root>
EOF
@b1 = @document.children[0].children[0] @a1 = @document.at_xpath('root/a[1]')
@b2 = @document.children[0].children[1] @a2 = @document.at_xpath('root/b/a[1]')
end end
example 'evaluate a predicate that returns the first <b> node' do example 'return a node set containing all first <a> nodes' do
evaluate_xpath(@document, 'root/b[1]').should == node_set(@b1) evaluate_xpath(@document, 'descendant-or-self::node()/a[1]')
end .should == node_set(@a1, @a2)
example 'evaluate a predicate that returns the second <b> node' do
evaluate_xpath(@document, 'root/b[2]').should == node_set(@b2)
end end
end end
end end