diff --git a/lib/oga/xpath/evaluator.rb b/lib/oga/xpath/evaluator.rb index 5cc311e..ba79ae6 100644 --- a/lib/oga/xpath/evaluator.rb +++ b/lib/oga/xpath/evaluator.rb @@ -101,9 +101,7 @@ module Oga nodes = XML::NodeSet.new context.each do |xml_node| - if can_match_node?(xml_node) and node_matches?(xml_node, ast_node) - nodes << xml_node - end + nodes << xml_node if node_matches?(xml_node, ast_node) end return nodes @@ -144,7 +142,7 @@ module Oga while has_parent?(xml_node) xml_node = xml_node.parent - if can_match_node?(xml_node) and node_matches?(xml_node, ast_node) + if node_matches?(xml_node, ast_node) nodes << xml_node break end @@ -265,8 +263,43 @@ module Oga next unless check - if can_match_node?(doc_node) and node_matches?(doc_node, ast_node) + nodes << doc_node if node_matches?(doc_node, ast_node) + end + end + + return nodes + end + + ## + # Evaluates the `following-sibling` axis. + # + # @param [Oga::XPath::Node] ast_node + # @param [Oga::XML::NodeSet] context + # @return [Oga::XML::NodeSet] + # + def on_axis_following_sibling(ast_node, context) + nodes = XML::NodeSet.new + + context.each do |context_node| + check = false + parent = context_node.respond_to?(:parent) ? context_node.parent : nil + + @document.each_node do |doc_node| + # Skip child nodes of the current context node, compare all + # following nodes. + if doc_node == context_node + check = true + throw :skip_children + end + + if !check or parent != doc_node.parent + next + end + + if node_matches?(doc_node, ast_node) nodes << doc_node + + throw :skip_children end end end @@ -307,6 +340,8 @@ module Oga # @return [Oga::XML::NodeSet] # def node_matches?(xml_node, ast_node) + return false unless can_match_node?(xml_node) + ns, name = *ast_node name_matches = xml_node.name == name || name == '*' diff --git a/spec/oga/xpath/evaluator/axes/following_sibling_spec.rb b/spec/oga/xpath/evaluator/axes/following_sibling_spec.rb new file mode 100644 index 0000000..be246c4 --- /dev/null +++ b/spec/oga/xpath/evaluator/axes/following_sibling_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Oga::XPath::Evaluator do + context 'following-sibling axis' do + before do + # Strip whitespace so it's easier to retrieve/compare elements. + @document = parse(<<-EOF.strip.gsub(/\s+/m, '')) + + + + + + + + + + EOF + + @first_baz = @document.children[0].children[0].children[1] + @second_baz = @first_baz.children[0] + @third_baz = @document.children[0].children[1] + @evaluator = described_class.new(@document) + end + + # This should return an empty set since the document doesn't have any + # following nodes. + context 'using a document as the root' do + before do + @set = @evaluator.evaluate('following-sibling::foo') + end + + it_behaves_like :empty_node_set + end + + context 'matching nodes in the current context' do + before do + @set = @evaluator.evaluate('root/foo/following-sibling::baz') + end + + it_behaves_like :node_set, :length => 1 + + example 'return the third node' do + @set[0].should == @third_baz + end + end + + context 'matching nodes in other contexts' do + before do + @set = @evaluator.evaluate('root/foo/bar/following-sibling::baz') + end + + it_behaves_like :node_set, :length => 1 + + example 'return the first node' do + @set[0].should == @first_baz + end + end + end +end diff --git a/spec/oga/xpath/evaluator/general_spec.rb b/spec/oga/xpath/evaluator/general_spec.rb index a6222f5..5424def 100644 --- a/spec/oga/xpath/evaluator/general_spec.rb +++ b/spec/oga/xpath/evaluator/general_spec.rb @@ -61,6 +61,12 @@ describe Oga::XPath::Evaluator do example 'return true if a node is matched without having a namespace' do @evaluator.node_matches?(@name_node, s(:test, '*', 'a')).should == true end + + example 'return false when trying to match an XML::Text instance' do + text = Oga::XML::Text.new(:text => 'Foobar') + + @evaluator.node_matches?(text, s(:test, nil, 'a')).should == false + end end context '#can_match_node?' do