From: Kouhei Sutou Date: 2006-12-30T13:00:36+09:00 Subject: REXML 3.1.6 has XPath problems ----Next_Part(Sat_Dec_30_13_00_32_2006_860)-- Content-Type: Multipart/Mixed; boundary="--Next_Part(Sat_Dec_30_13_00_32_2006_860)--" ----Next_Part(Sat_Dec_30_13_00_32_2006_860)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Hi, I found some bugs of REXML 3.1.6 related XPath. The following code raises an exception: require 'rexml/document' source = <<-XML TEXT1 TEXT2 XML doc = REXML::Document.new(source) predicate = "count(child::node()|following-sibling::node()|preceding-sibling::node())=0" p REXML::XPath.match(doc, "/descendant-or-self::node()[#{predicate}]") The exception is the following: /usr/lib/ruby/1.8/rexml/xpath_parser.rb:335:in `expr': undefined method `children' for nil:NilClass (NoMethodError) from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:334:in `each' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:334:in `expr' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:422:in `expr' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:421:in `expr' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:447:in `expr' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:445:in `collect' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:445:in `expr' from /usr/lib/ruby/1.8/rexml/encoding.rb:47:in `each_with_index' ... 14 levels... from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:125:in `match' from /usr/lib/ruby/1.8/rexml/xpath_parser.rb:56:in `parse' from /usr/lib/ruby/1.8/rexml/xpath.rb:63:in `match' from rexml-3.1.6-xpath-bug.rb:21 I'll attach a patch of test case and fixing. Thanks, -- kou ----Next_Part(Sat_Dec_30_13_00_32_2006_860)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="rexml-3.1.6-siblings-bug.diff" diff -ru rexml_3.1.6.orig/src/rexml/xpath_parser.rb rexml_3.1.6/src/rexml/xpath_parser.rb --- rexml_3.1.6.orig/src/rexml/xpath_parser.rb 2006-11-28 22:54:57.000000000 +0900 +++ rexml_3.1.6/src/rexml/xpath_parser.rb 2006-12-30 12:55:45.000000000 +0900 @@ -351,7 +351,8 @@ when :following_sibling #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}" results = [] - for node in nodeset + nodeset.each do |node| + next if node.parent.nil? all_siblings = node.parent.children current_index = all_siblings.index( node ) following_siblings = all_siblings[ current_index+1 .. -1 ] @@ -362,13 +363,14 @@ when :preceding_sibling results = [] - for node in nodeset + nodeset.each do |node| + next if node.parent.nil? all_siblings = node.parent.children current_index = all_siblings.index( node ) - preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse - #results += expr( path_stack.dclone, preceding_siblings ) + preceding_siblings = all_siblings[ 0, current_index ].reverse + results += preceding_siblings end - nodeset = preceding_siblings || [] + nodeset = results node_types = ELEMENTS when :preceding diff -ru rexml_3.1.6.orig/test/xpath_test_pred.rb rexml_3.1.6/test/xpath_test_pred.rb --- rexml_3.1.6.orig/test/xpath_test_pred.rb 2006-11-28 22:54:56.000000000 +0900 +++ rexml_3.1.6/test/xpath_test_pred.rb 2006-12-30 12:53:44.000000000 +0900 @@ -54,4 +54,27 @@ #puts path, @parser.parse( path ).inspect return m end + + def test_get_no_siblings_terminal_nodes + source = <<-XML + + TEXT1 + + + + TEXT2 + + + +XML + doc = REXML::Document.new(source) + predicate = "count(child::node()|" + + "following-sibling::node()|" + + "preceding-sibling::node())=0" + m = REXML::XPath.match(doc, "/descendant-or-self::node()[#{predicate}]") + assert_equal( [REXML::Text.new("TEXT1"), + REXML::Text.new("TEXT2"), + REXML::Comment.new("COMMENT")], + m ) + end end ----Next_Part(Sat_Dec_30_13_00_32_2006_860)---- ----Next_Part(Sat_Dec_30_13_00_32_2006_860)----