ruby-changes:50997
From: kou <ko1@a...>
Date: Fri, 20 Apr 2018 06:34:46 +0900 (JST)
Subject: [ruby-changes:50997] kou:r63204 (trunk): rexml: Fix a XPath bug that white spaces aren't ignored
kou 2018-04-20 06:34:40 +0900 (Fri, 20 Apr 2018) New Revision: 63204 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63204 Log: rexml: Fix a XPath bug that white spaces aren't ignored lib/rexml/parsers/xpathparser.rb: Ignore white spaces in relative location path. test/rexml/xpath/test_base.rb: Add more test patterns and use more debug friendly assertion style. Modified files: trunk/lib/rexml/parsers/xpathparser.rb trunk/test/rexml/xpath/test_base.rb Index: test/rexml/xpath/test_base.rb =================================================================== --- test/rexml/xpath/test_base.rb (revision 63203) +++ test/rexml/xpath/test_base.rb (revision 63204) @@ -632,29 +632,36 @@ module REXMLTests https://github.com/ruby/ruby/blob/trunk/test/rexml/xpath/test_base.rb#L632 <c id='a'/> </b> <c id='b'/> + <c id='c'/> + <c/> </a>") - assert_equal( 1, REXML::XPath.match(doc, - "//*[local-name()='c' and @id='b']").size ) - assert_equal( 1, REXML::XPath.match(doc, - "//*[ local-name()='c' and @id='b' ]").size ) - assert_equal( 1, REXML::XPath.match(doc, - "//*[ local-name() = 'c' and @id = 'b' ]").size ) - assert_equal( 1, - REXML::XPath.match(doc, '/a/c[@id]').size ) - assert_equal( 1, - REXML::XPath.match(doc, '/a/c[(@id)]').size ) - assert_equal( 1, - REXML::XPath.match(doc, '/a/c[ @id ]').size ) - assert_equal( 1, - REXML::XPath.match(doc, '/a/c[ (@id) ]').size ) - assert_equal( 1, - REXML::XPath.match(doc, '/a/c[( @id )]').size ) - assert_equal( 1, REXML::XPath.match(doc.root, - '/a/c[ ( @id ) ]').size ) - assert_equal( 1, REXML::XPath.match(doc, - '/a/c [ ( @id ) ] ').size ) - assert_equal( 1, REXML::XPath.match(doc, - ' / a / c [ ( @id ) ] ').size ) + match = lambda do |xpath| + REXML::XPath.match(doc, xpath).collect(&:to_s) + end + assert_equal(["<c id='b'/>"], + match.call("//*[local-name()='c' and @id='b']")) + assert_equal(["<c id='b'/>"], + match.call("//*[ local-name()='c' and @id='b' ]")) + assert_equal(["<c id='b'/>"], + match.call("//*[ local-name() = 'c' and @id = 'b' ]")) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[@id]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[(@id)]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[ @id ]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[ (@id) ]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[( @id )]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c[ ( @id ) ]')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/a/c [ ( @id ) ] ')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call(' / a / c [ ( @id ) ] ')) + assert_equal(["<c id='b'/>", "<c id='c'/>"], + match.call('/ a / child:: c [( @id )] /')) end def test_text_nodes Index: lib/rexml/parsers/xpathparser.rb =================================================================== --- lib/rexml/parsers/xpathparser.rb (revision 63203) +++ lib/rexml/parsers/xpathparser.rb (revision 63204) @@ -185,7 +185,7 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L185 # | '/' RelativeLocationPath? # | '//' RelativeLocationPath def LocationPath path, parsed - path = path.strip + path = path.lstrip if path[0] == ?/ parsed << :document if path[1] == ?/ @@ -209,7 +209,12 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L209 # | RelativeLocationPath '//' Step AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/ def RelativeLocationPath path, parsed - while path.size > 0 + loop do + original_path = path + path = path.lstrip + + return original_path if path.empty? + # (axis or @ or <child::>) nodetest predicate > # OR > / Step # (. or ..) > @@ -239,28 +244,25 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L244 n = [] path = NodeTest( path, n) - if path[0] == ?[ - path = Predicate( path, n ) - end + path = Predicate( path, n ) parsed.concat(n) end - if path.size > 0 - if path[0] == ?/ - if path[1] == ?/ - parsed << :descendant_or_self - parsed << :node - path = path[2..-1] - else - path = path[1..-1] - end - else - return path - end + original_path = path + path = path.lstrip + return original_path if path.empty? + + return original_path if path[0] != ?/ + + if path[1] == ?/ + parsed << :descendant_or_self + parsed << :node + path = path[2..-1] + else + path = path[1..-1] end end - return path end # Returns a 1-1 map of the nodeset @@ -277,6 +279,8 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L279 NODE_TYPE = /^(comment|text|node)\(\s*\)/m PI = /^processing-instruction\(/ def NodeTest path, parsed + original_path = path + path = path.lstrip case path when /^\*/ path = $' @@ -310,13 +314,17 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L314 parsed << :qname parsed << prefix parsed << name + else + path = original_path end return path end # Filters the supplied nodeset on the predicate(s) def Predicate path, parsed - return nil unless path[0] == ?[ + original_path = path + path = path.lstrip + return original_path unless path[0] == ?[ predicates = [] while path[0] == ?[ path, expr = get_group(path) @@ -509,8 +517,7 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L517 #| LocationPath #| FilterExpr ('/' | '//') RelativeLocationPath def PathExpr path, parsed - path =~ /^\s*/ - path = $' + path = path.lstrip n = [] rest = FilterExpr( path, n ) if rest != path @@ -530,7 +537,7 @@ module REXML https://github.com/ruby/ruby/blob/trunk/lib/rexml/parsers/xpathparser.rb#L537 def FilterExpr path, parsed n = [] path = PrimaryExpr( path, n ) - path = Predicate(path, n) if path and path[0] == ?[ + path = Predicate(path, n) parsed.concat(n) path end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/