[ruby/rexml] xpath: add missing value conversions for equality and relational expressions
GitHub: fix #18 Reported by Mirko Budszuhn. Thanks!!! https://github.com/ruby/rexml/commit/0dca2a2ba0
This commit is contained in:
parent
39f275edf7
commit
310a2a9860
@ -864,32 +864,51 @@ module REXML
|
|||||||
# Else, convert to string
|
# Else, convert to string
|
||||||
# Else
|
# Else
|
||||||
# Convert both to numbers and compare
|
# Convert both to numbers and compare
|
||||||
set1 = unnode(set1) if set1.is_a?(Array)
|
compare(set1, op, set2)
|
||||||
set2 = unnode(set2) if set2.is_a?(Array)
|
|
||||||
s1 = Functions.string(set1)
|
|
||||||
s2 = Functions.string(set2)
|
|
||||||
if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
|
|
||||||
set1 = Functions::boolean( set1 )
|
|
||||||
set2 = Functions::boolean( set2 )
|
|
||||||
else
|
|
||||||
if op == :eq or op == :neq
|
|
||||||
if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
|
|
||||||
set1 = Functions::number( s1 )
|
|
||||||
set2 = Functions::number( s2 )
|
|
||||||
else
|
|
||||||
set1 = Functions::string( set1 )
|
|
||||||
set2 = Functions::string( set2 )
|
|
||||||
end
|
|
||||||
else
|
|
||||||
set1 = Functions::number( set1 )
|
|
||||||
set2 = Functions::number( set2 )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
compare( set1, op, set2 )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def value_type(value)
|
||||||
|
case value
|
||||||
|
when true, false
|
||||||
|
:boolean
|
||||||
|
when Numeric
|
||||||
|
:number
|
||||||
|
when String
|
||||||
|
:string
|
||||||
|
else
|
||||||
|
raise "[BUG] Unexpected value type: <#{value.inspect}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize_compare_values(a, operator, b)
|
||||||
|
a_type = value_type(a)
|
||||||
|
b_type = value_type(b)
|
||||||
|
case operator
|
||||||
|
when :eq, :neq
|
||||||
|
if a_type == :boolean or b_type == :boolean
|
||||||
|
a = Functions.boolean(a) unless a_type == :boolean
|
||||||
|
b = Functions.boolean(b) unless b_type == :boolean
|
||||||
|
elsif a_type == :number or b_type == :number
|
||||||
|
a = Functions.number(a) unless a_type == :number
|
||||||
|
b = Functions.number(b) unless b_type == :number
|
||||||
|
else
|
||||||
|
a = Functions.string(a) unless a_type == :string
|
||||||
|
b = Functions.string(b) unless b_type == :string
|
||||||
|
end
|
||||||
|
when :lt, :lteq, :gt, :gteq
|
||||||
|
a = Functions.number(a) unless a_type == :number
|
||||||
|
b = Functions.number(b) unless b_type == :number
|
||||||
|
else
|
||||||
|
message = "[BUG] Unexpected compare operator: " +
|
||||||
|
"<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
|
||||||
|
raise message
|
||||||
|
end
|
||||||
|
[a, b]
|
||||||
|
end
|
||||||
|
|
||||||
def compare(a, operator, b)
|
def compare(a, operator, b)
|
||||||
|
a, b = normalize_compare_values(a, operator, b)
|
||||||
case operator
|
case operator
|
||||||
when :eq
|
when :eq
|
||||||
a == b
|
a == b
|
||||||
|
256
test/rexml/xpath/test_compare.rb
Normal file
256
test/rexml/xpath/test_compare.rb
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
# frozen_string_literal: false
|
||||||
|
|
||||||
|
require_relative "../rexml_test_utils"
|
||||||
|
|
||||||
|
require "rexml/document"
|
||||||
|
|
||||||
|
module REXMLTests
|
||||||
|
class TestXPathCompare < Test::Unit::TestCase
|
||||||
|
def match(xml, xpath)
|
||||||
|
document = REXML::Document.new(xml)
|
||||||
|
REXML::XPath.match(document, xpath)
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestEqual < self
|
||||||
|
class TestNodeSet < self
|
||||||
|
def test_boolean_true
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child/>
|
||||||
|
<child/>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child=true()"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_boolean_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child=true()"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_true
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child=100"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child=300"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_true
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>text</child>
|
||||||
|
<child>string</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child='string'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>text</child>
|
||||||
|
<child>string</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child='nonexistent'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestBoolean < self
|
||||||
|
def test_number_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "true()=1"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "true()=0"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "true()='string'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "true()=''"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestNumber < self
|
||||||
|
def test_string_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "1='1'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "1='2'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestGreaterThan < self
|
||||||
|
class TestNodeSet < self
|
||||||
|
def test_boolean_truex
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child/>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child>false()"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_boolean_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child/>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child>true()"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_true
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child>199"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child>200"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_true
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "/root/child>'199'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = <<-XML
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root>
|
||||||
|
<child>100</child>
|
||||||
|
<child>200</child>
|
||||||
|
</root>
|
||||||
|
XML
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "/root/child>'200'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestBoolean < self
|
||||||
|
def test_string_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "true()>'0'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "true()>'1'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestNumber < self
|
||||||
|
def test_boolean_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "true()>0"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_number_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "true()>1"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "1>'0'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "1>'1'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestString < self
|
||||||
|
def test_string_true
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([true],
|
||||||
|
match(xml, "'1'>'0'"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_string_false
|
||||||
|
xml = "<root/>"
|
||||||
|
assert_equal([false],
|
||||||
|
match(xml, "'1'>'1'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,84 +0,0 @@
|
|||||||
# frozen_string_literal: false
|
|
||||||
|
|
||||||
require_relative "../rexml_test_utils"
|
|
||||||
|
|
||||||
require "rexml/document"
|
|
||||||
|
|
||||||
module REXMLTests
|
|
||||||
class TestXPathNodeSet < Test::Unit::TestCase
|
|
||||||
def match(xml, xpath)
|
|
||||||
document = REXML::Document.new(xml)
|
|
||||||
REXML::XPath.match(document, xpath)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_boolean_true
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<child/>
|
|
||||||
<child/>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([true],
|
|
||||||
match(xml, "/root/child=true()"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_boolean_false
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([false],
|
|
||||||
match(xml, "/root/child=true()"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_number_true
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<child>100</child>
|
|
||||||
<child>200</child>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([true],
|
|
||||||
match(xml, "/root/child=100"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_number_false
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<child>100</child>
|
|
||||||
<child>200</child>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([false],
|
|
||||||
match(xml, "/root/child=300"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_string_true
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<child>text</child>
|
|
||||||
<child>string</child>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([true],
|
|
||||||
match(xml, "/root/child='string'"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_string_false
|
|
||||||
xml = <<-XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<root>
|
|
||||||
<child>text</child>
|
|
||||||
<child>string</child>
|
|
||||||
</root>
|
|
||||||
XML
|
|
||||||
assert_equal([false],
|
|
||||||
match(xml, "/root/child='nonexistent'"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
x
Reference in New Issue
Block a user