summaryrefslogtreecommitdiff
path: root/lib/rexml/xpath_parser.rb
diff options
context:
space:
mode:
authorKouhei Sutou <[email protected]>2019-05-25 17:06:53 +0900
committerHiroshi SHIBATA <[email protected]>2019-08-04 11:55:31 +0900
commit6ef82943978ea5816a91c32e9ff822c73d1935f9 ()
treef04b130680d6817b4941e74d212df6faccd02e9a /lib/rexml/xpath_parser.rb
parentc46ba8e9a3b1b6c13232c1af3e9f2efd4a3eec98 (diff)
[ruby/rexml] xpath: fix a bug for equality or relational expressions
: fix #17 There is a bug when they are used against node set. They should return boolean value but they returned node set. Reported by Mirko Budszuhn. Thanks!!! https://.com/ruby/rexml/commit/a02bf38440
-rw-r--r--lib/rexml/xpath_parser.rb163
1 files changed, 86 insertions, 77 deletions
@@ -5,7 +5,6 @@ require "pp"
require_relative 'namespace'
require_relative 'xmltokens'
require_relative 'attribute'
-require_relative 'syncenumerator'
require_relative 'parsers/xpathparser'
class Object
@@ -141,7 +140,7 @@ module REXML
when Array # nodeset
unnode(result)
else
- result
end
end
@@ -341,26 +340,24 @@ module REXML
var_name = path_stack.shift
return [@variables[var_name]]
- # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
- # TODO: Special case for :or and :and -- not evaluate the right
- # operand if the left alone determines result (i.e. is true for
- # :or and false for :and).
- when :eq, :neq, :lt, :lteq, :gt, :gteq, :or
left = expr( path_stack.shift, nodeset.dup, context )
right = expr( path_stack.shift, nodeset.dup, context )
res = equality_relational_compare( left, op, right )
trace(op, left, right, res) if @debug
return res
when :and
- left = expr( path_stack.shift, nodeset.dup, context )
- return [] unless left
- if left.respond_to?(:inject) and !left.inject(false) {|a,b| a | b}
- return []
- end
- right = expr( path_stack.shift, nodeset.dup, context )
- res = equality_relational_compare( left, op, right )
- return res
when :div, :mod, :mult, :plus, :minus
left = expr(path_stack.shift, nodeset, context)
@@ -397,31 +394,34 @@ module REXML
when :function
func_name = path_stack.shift.tr('-','_')
arguments = path_stack.shift
- subcontext = context ? nil : { :size => nodeset.size }
-
- res = []
- cont = context
- nodeset.each_with_index do |node, i|
- if subcontext
- if node.is_a?(XPathNode)
- subcontext[:node] = node.raw_node
- subcontext[:index] = node.position
- else
- subcontext[:node] = node
- subcontext[:index] = i
- end
- cont = subcontext
- end
- arg_clone = arguments.dclone
- args = arg_clone.collect do |arg|
- result = expr( arg, [node], cont )
- result = unnode(result) if result.is_a?(Array)
- result
end
- Functions.context = cont
- res << Functions.send( func_name, *args )
end
- return res
else
raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
@@ -806,31 +806,28 @@ module REXML
end
end
- def equality_relational_compare( set1, op, set2 )
set1 = unnode(set1) if set1.is_a?(Array)
set2 = unnode(set2) if set2.is_a?(Array)
if set1.kind_of? Array and set2.kind_of? Array
- if set1.size == 0 or set2.size == 0
- nd = set1.size==0 ? set2 : set1
- rv = nd.collect { |il| compare( il, op, nil ) }
- return rv
- else
- res = []
- SyncEnumerator.new( set1, set2 ).each { |i1, i2|
- i1 = norm( i1 )
- i2 = norm( i2 )
- res << compare( i1, op, i2 )
- }
- return res
end
- end
- # If one is nodeset and other is number, compare number to each item
- # in nodeset s.t. number op number(string(item))
- # If one is nodeset and other is string, compare string to each item
- # in nodeset s.t. string op string(item)
- # If one is nodeset and other is boolean, compare boolean to each item
- # in nodeset s.t. boolean op boolean(item)
- if set1.kind_of? Array or set2.kind_of? Array
if set1.kind_of? Array
a = set1
b = set2
@@ -841,15 +838,23 @@ module REXML
case b
when true, false
- return unnode(a) {|v| compare( Functions::boolean(v), op, b ) }
when Numeric
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
- when /^\d+(\.\d+)?$/
- b = Functions::number( b )
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
else
- b = Functions::string( b )
- return unnode(a) { |v| compare( Functions::string(v), op, b ) }
end
else
# If neither is nodeset,
@@ -880,13 +885,12 @@ module REXML
set2 = Functions::number( set2 )
end
end
- return compare( set1, op, set2 )
end
- return false
end
- def compare a, op, b
- case op
when :eq
a == b
when :neq
@@ -899,22 +903,27 @@ module REXML
a > b
when :gteq
a >= b
- when :and
- a and b
- when :or
- a or b
else
- false
end
end
- def unnode(nodeset)
- nodeset.collect do |node|
if node.is_a?(XPathNode)
unnoded = node.raw_node
else
unnoded = node
end
unnoded = yield(unnoded) if block_given?
unnoded
end