rexml 3.2.1 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rexml might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8154ea2369b6221818ab5eadd19d65464174dad7d4d1d63ab4b8b31ba475f701
4
- data.tar.gz: 36e1512091b57d3724b379d77180ba715bb15e5d988c0f0aa129d78b662ae472
3
+ metadata.gz: 80a799893d66ff50bf8f93d6278b7243d29f1fcdc9d3e1b34ddbb3d044e288c8
4
+ data.tar.gz: c78162208507f9aa9dfbb7596d39fc3afbc99d244837b70b96f8477470ce7cc0
5
5
  SHA512:
6
- metadata.gz: 883b2fbadd2d2967dae6eaf1285f518749cb995c0e8a5fa1ba7c644079e6e3acc49f6df108317b1d97cd03c84e074e0aba78bb259f0b658d27309cce78ba2a94
7
- data.tar.gz: a0e1a2e4f7af8bfd6639929a7747d34e529783ffff986b0ae5e5d91b6ba10e5bfc07c2c5f9a387fea9632f1d4dddffcf9a04eabbcca91a2611e4c3dc83e7834b
6
+ metadata.gz: c0a6d3cd983aff3f4427dfa6e50e06b9720e2b704c55133d99ce9b7e81daeca038a5464341df81ada5322bf16b60c38e40d5301d90a659ed8a2a983db074e132
7
+ data.tar.gz: e261132e4c5389c0ac9f489f5dda410c4980b75a27166329faf94b316d966ff73e6dc1e79ee131270efbf52209924db193973cf22ea8e9e204039ca8773fa8fa
data/NEWS.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # News
2
2
 
3
+ ## 3.2.2 - 2019-06-03 {#version-3-2-2}
4
+
5
+ ### Fixes
6
+
7
+ * xpath: Fixed a bug for equality and relational expressions.
8
+ [GitHub#17][Reported by Mirko Budszuhn]
9
+
10
+ * xpath: Fixed `boolean()` implementation.
11
+
12
+ * xpath: Fixed `local_name()` with nonexistent node.
13
+
14
+ * xpath: Fixed `number()` implementation with node set.
15
+ [GitHub#18][Reported by Mirko Budszuhn]
16
+
17
+ ### Thanks
18
+
19
+ * Mirko Budszuhn
20
+
3
21
  ## 3.2.1 - 2019-05-04 {#version-3-2-1}
4
22
 
5
23
  ### Improvements
@@ -66,11 +66,11 @@ module REXML
66
66
  def Functions::id( object )
67
67
  end
68
68
 
69
- # UNTESTED
70
- def Functions::local_name( node_set=nil )
71
- get_namespace( node_set ) do |node|
69
+ def Functions::local_name(node_set=nil)
70
+ get_namespace(node_set) do |node|
72
71
  return node.local_name
73
72
  end
73
+ ""
74
74
  end
75
75
 
76
76
  def Functions::namespace_uri( node_set=nil )
@@ -315,18 +315,23 @@ module REXML
315
315
  end
316
316
  end
317
317
 
318
- # UNTESTED
319
- def Functions::boolean( object=nil )
320
- if object.kind_of? String
321
- if object =~ /\d+/u
322
- return object.to_f != 0
323
- else
324
- return object.size > 0
325
- end
326
- elsif object.kind_of? Array
327
- object = object.find{|x| x and true}
318
+ def Functions::boolean(object=@@context[:node])
319
+ case object
320
+ when true, false
321
+ object
322
+ when Float
323
+ return false if object.zero?
324
+ return false if object.nan?
325
+ true
326
+ when Numeric
327
+ not object.zero?
328
+ when String
329
+ not object.empty?
330
+ when Array
331
+ not object.empty?
332
+ else
333
+ object ? true : false
328
334
  end
329
- return object ? true : false
330
335
  end
331
336
 
332
337
  # UNTESTED
@@ -380,25 +385,23 @@ module REXML
380
385
  #
381
386
  # an object of a type other than the four basic types is converted to a
382
387
  # number in a way that is dependent on that type
383
- def Functions::number( object=nil )
384
- object = @@context[:node] unless object
388
+ def Functions::number(object=@@context[:node])
385
389
  case object
386
390
  when true
387
391
  Float(1)
388
392
  when false
389
393
  Float(0)
390
394
  when Array
391
- number(string( object ))
395
+ number(string(object))
392
396
  when Numeric
393
397
  object.to_f
394
398
  else
395
- str = string( object )
396
- # If XPath ever gets scientific notation...
397
- #if str =~ /^\s*-?(\d*\.?\d+|\d+\.)([Ee]\d*)?\s*$/
398
- if str =~ /^\s*-?(\d*\.?\d+|\d+\.)\s*$/
399
- str.to_f
399
+ str = string(object)
400
+ case str.strip
401
+ when /\A\s*(-?(?:\d+(?:\.\d*)?|\.\d+))\s*\z/
402
+ $1.to_f
400
403
  else
401
- (0.0 / 0.0)
404
+ Float::NAN
402
405
  end
403
406
  end
404
407
  end
@@ -24,7 +24,7 @@
24
24
  module REXML
25
25
  COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
26
26
  DATE = "2008/019"
27
- VERSION = "3.2.1"
27
+ VERSION = "3.2.2"
28
28
  REVISION = ""
29
29
 
30
30
  Copyright = COPYRIGHT
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: false
2
+
3
+ require "pp"
4
+
2
5
  require_relative 'namespace'
3
6
  require_relative 'xmltokens'
4
7
  require_relative 'attribute'
5
- require_relative 'syncenumerator'
6
8
  require_relative 'parsers/xpathparser'
7
9
 
8
10
  class Object
@@ -47,7 +49,10 @@ module REXML
47
49
  include XMLTokens
48
50
  LITERAL = /^'([^']*)'|^"([^"]*)"/u
49
51
 
52
+ DEBUG = (ENV["REXML_XPATH_PARSER_DEBUG"] == "true")
53
+
50
54
  def initialize(strict: false)
55
+ @debug = DEBUG
51
56
  @parser = REXML::Parsers::XPathParser.new
52
57
  @namespaces = nil
53
58
  @variables = {}
@@ -135,7 +140,7 @@ module REXML
135
140
  when Array # nodeset
136
141
  unnode(result)
137
142
  else
138
- result
143
+ [result]
139
144
  end
140
145
  end
141
146
 
@@ -162,10 +167,10 @@ module REXML
162
167
  # Expr takes a stack of path elements and a set of nodes (either a Parent
163
168
  # or an Array and returns an Array of matching nodes
164
169
  def expr( path_stack, nodeset, context=nil )
165
- # enter(:expr, path_stack, nodeset)
170
+ enter(:expr, path_stack, nodeset) if @debug
166
171
  return nodeset if path_stack.length == 0 || nodeset.length == 0
167
172
  while path_stack.length > 0
168
- # trace(:while, path_stack, nodeset)
173
+ trace(:while, path_stack, nodeset) if @debug
169
174
  if nodeset.length == 0
170
175
  path_stack.clear
171
176
  return []
@@ -184,7 +189,7 @@ module REXML
184
189
  child(nodeset)
185
190
  end
186
191
  when :literal
187
- # trace(:literal, path_stack, nodeset)
192
+ trace(:literal, path_stack, nodeset) if @debug
188
193
  return path_stack.shift
189
194
  when :attribute
190
195
  nodeset = step(path_stack, any_type: :attribute) do
@@ -335,26 +340,24 @@ module REXML
335
340
  var_name = path_stack.shift
336
341
  return [@variables[var_name]]
337
342
 
338
- # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
339
- # TODO: Special case for :or and :and -- not evaluate the right
340
- # operand if the left alone determines result (i.e. is true for
341
- # :or and false for :and).
342
- when :eq, :neq, :lt, :lteq, :gt, :gteq, :or
343
+ when :eq, :neq, :lt, :lteq, :gt, :gteq
343
344
  left = expr( path_stack.shift, nodeset.dup, context )
344
345
  right = expr( path_stack.shift, nodeset.dup, context )
345
346
  res = equality_relational_compare( left, op, right )
346
- # trace(op, left, right, res)
347
+ trace(op, left, right, res) if @debug
347
348
  return res
348
349
 
350
+ when :or
351
+ left = expr(path_stack.shift, nodeset.dup, context)
352
+ return true if Functions.boolean(left)
353
+ right = expr(path_stack.shift, nodeset.dup, context)
354
+ return Functions.boolean(right)
355
+
349
356
  when :and
350
- left = expr( path_stack.shift, nodeset.dup, context )
351
- return [] unless left
352
- if left.respond_to?(:inject) and !left.inject(false) {|a,b| a | b}
353
- return []
354
- end
355
- right = expr( path_stack.shift, nodeset.dup, context )
356
- res = equality_relational_compare( left, op, right )
357
- return res
357
+ left = expr(path_stack.shift, nodeset.dup, context)
358
+ return false unless Functions.boolean(left)
359
+ right = expr(path_stack.shift, nodeset.dup, context)
360
+ return Functions.boolean(right)
358
361
 
359
362
  when :div, :mod, :mult, :plus, :minus
360
363
  left = expr(path_stack.shift, nodeset, context)
@@ -391,45 +394,48 @@ module REXML
391
394
  when :function
392
395
  func_name = path_stack.shift.tr('-','_')
393
396
  arguments = path_stack.shift
394
- subcontext = context ? nil : { :size => nodeset.size }
395
-
396
- res = []
397
- cont = context
398
- nodeset.each_with_index do |node, i|
399
- if subcontext
400
- if node.is_a?(XPathNode)
401
- subcontext[:node] = node.raw_node
402
- subcontext[:index] = node.position
403
- else
404
- subcontext[:node] = node
405
- subcontext[:index] = i
406
- end
407
- cont = subcontext
408
- end
409
- arg_clone = arguments.dclone
410
- args = arg_clone.collect do |arg|
411
- result = expr( arg, [node], cont )
412
- result = unnode(result) if result.is_a?(Array)
413
- result
397
+
398
+ if nodeset.size != 1
399
+ message = "[BUG] Node set size must be 1 for function call: "
400
+ message += "<#{func_name}>: <#{nodeset.inspect}>: "
401
+ message += "<#{arguments.inspect}>"
402
+ raise message
403
+ end
404
+
405
+ node = nodeset.first
406
+ if context
407
+ target_context = context
408
+ else
409
+ target_context = {:size => nodeset.size}
410
+ if node.is_a?(XPathNode)
411
+ target_context[:node] = node.raw_node
412
+ target_context[:index] = node.position
413
+ else
414
+ target_context[:node] = node
415
+ target_context[:index] = 1
414
416
  end
415
- Functions.context = cont
416
- res << Functions.send( func_name, *args )
417
417
  end
418
- return res
418
+ args = arguments.dclone.collect do |arg|
419
+ result = expr(arg, nodeset, target_context)
420
+ result = unnode(result) if result.is_a?(Array)
421
+ result
422
+ end
423
+ Functions.context = target_context
424
+ return Functions.send(func_name, *args)
419
425
 
420
426
  else
421
427
  raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
422
428
  end
423
429
  end # while
424
430
  return nodeset
425
- # ensure
426
- # leave(:expr, path_stack, nodeset)
431
+ ensure
432
+ leave(:expr, path_stack, nodeset) if @debug
427
433
  end
428
434
 
429
435
  def step(path_stack, any_type: :element, order: :forward)
430
436
  nodesets = yield
431
437
  begin
432
- # enter(:step, path_stack, nodesets)
438
+ enter(:step, path_stack, nodesets) if @debug
433
439
  nodesets = node_test(path_stack, nodesets, any_type: any_type)
434
440
  while path_stack[0] == :predicate
435
441
  path_stack.shift # :predicate
@@ -457,13 +463,13 @@ module REXML
457
463
  new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
458
464
  end
459
465
  new_nodeset
460
- # ensure
461
- # leave(:step, path_stack, new_nodeset)
466
+ ensure
467
+ leave(:step, path_stack, new_nodeset) if @debug
462
468
  end
463
469
  end
464
470
 
465
471
  def node_test(path_stack, nodesets, any_type: :element)
466
- # enter(:node_test, path_stack, nodesets)
472
+ enter(:node_test, path_stack, nodesets) if @debug
467
473
  operator = path_stack.shift
468
474
  case operator
469
475
  when :qname
@@ -563,8 +569,8 @@ module REXML
563
569
  raise message
564
570
  end
565
571
  new_nodesets
566
- # ensure
567
- # leave(:node_test, path_stack, new_nodesets)
572
+ ensure
573
+ leave(:node_test, path_stack, new_nodesets) if @debug
568
574
  end
569
575
 
570
576
  def filter_nodeset(nodeset)
@@ -577,7 +583,7 @@ module REXML
577
583
  end
578
584
 
579
585
  def evaluate_predicate(expression, nodesets)
580
- # enter(:predicate, expression, nodesets)
586
+ enter(:predicate, expression, nodesets) if @debug
581
587
  new_nodesets = nodesets.collect do |nodeset|
582
588
  new_nodeset = []
583
589
  subcontext = { :size => nodeset.size }
@@ -590,7 +596,7 @@ module REXML
590
596
  subcontext[:index] = index + 1
591
597
  end
592
598
  result = expr(expression.dclone, [node], subcontext)
593
- # trace(:predicate_evaluate, expression, node, subcontext, result)
599
+ trace(:predicate_evaluate, expression, node, subcontext, result) if @debug
594
600
  result = result[0] if result.kind_of? Array and result.length == 1
595
601
  if result.kind_of? Numeric
596
602
  if result == node.position
@@ -611,13 +617,15 @@ module REXML
611
617
  new_nodeset
612
618
  end
613
619
  new_nodesets
614
- # ensure
615
- # leave(:predicate, new_nodesets)
620
+ ensure
621
+ leave(:predicate, new_nodesets) if @debug
616
622
  end
617
623
 
618
624
  def trace(*args)
619
625
  indent = " " * @nest
620
- puts("#{indent}#{args.inspect}")
626
+ PP.pp(args, "").each_line do |line|
627
+ puts("#{indent}#{line}")
628
+ end
621
629
  end
622
630
 
623
631
  def enter(tag, *args)
@@ -798,31 +806,28 @@ module REXML
798
806
  end
799
807
  end
800
808
 
801
- def equality_relational_compare( set1, op, set2 )
809
+ def equality_relational_compare(set1, op, set2)
802
810
  set1 = unnode(set1) if set1.is_a?(Array)
803
811
  set2 = unnode(set2) if set2.is_a?(Array)
812
+
804
813
  if set1.kind_of? Array and set2.kind_of? Array
805
- if set1.size == 0 or set2.size == 0
806
- nd = set1.size==0 ? set2 : set1
807
- rv = nd.collect { |il| compare( il, op, nil ) }
808
- return rv
809
- else
810
- res = []
811
- SyncEnumerator.new( set1, set2 ).each { |i1, i2|
812
- i1 = norm( i1 )
813
- i2 = norm( i2 )
814
- res << compare( i1, op, i2 )
815
- }
816
- return res
814
+ # If both objects to be compared are node-sets, then the
815
+ # comparison will be true if and only if there is a node in the
816
+ # first node-set and a node in the second node-set such that the
817
+ # result of performing the comparison on the string-values of
818
+ # the two nodes is true.
819
+ set1.product(set2).any? do |node1, node2|
820
+ node_string1 = Functions.string(node1)
821
+ node_string2 = Functions.string(node2)
822
+ compare(node_string1, op, node_string2)
817
823
  end
818
- end
819
- # If one is nodeset and other is number, compare number to each item
820
- # in nodeset s.t. number op number(string(item))
821
- # If one is nodeset and other is string, compare string to each item
822
- # in nodeset s.t. string op string(item)
823
- # If one is nodeset and other is boolean, compare boolean to each item
824
- # in nodeset s.t. boolean op boolean(item)
825
- if set1.kind_of? Array or set2.kind_of? Array
824
+ elsif set1.kind_of? Array or set2.kind_of? Array
825
+ # If one is nodeset and other is number, compare number to each item
826
+ # in nodeset s.t. number op number(string(item))
827
+ # If one is nodeset and other is string, compare string to each item
828
+ # in nodeset s.t. string op string(item)
829
+ # If one is nodeset and other is boolean, compare boolean to each item
830
+ # in nodeset s.t. boolean op boolean(item)
826
831
  if set1.kind_of? Array
827
832
  a = set1
828
833
  b = set2
@@ -833,15 +838,23 @@ module REXML
833
838
 
834
839
  case b
835
840
  when true, false
836
- return unnode(a) {|v| compare( Functions::boolean(v), op, b ) }
841
+ each_unnode(a).any? do |unnoded|
842
+ compare(Functions.boolean(unnoded), op, b)
843
+ end
837
844
  when Numeric
838
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
839
- when /^\d+(\.\d+)?$/
840
- b = Functions::number( b )
841
- return unnode(a) {|v| compare( Functions::number(v), op, b )}
845
+ each_unnode(a).any? do |unnoded|
846
+ compare(Functions.number(unnoded), op, b)
847
+ end
848
+ when /\A\d+(\.\d+)?\z/
849
+ b = Functions.number(b)
850
+ each_unnode(a).any? do |unnoded|
851
+ compare(Functions.number(unnoded), op, b)
852
+ end
842
853
  else
843
- b = Functions::string( b )
844
- return unnode(a) { |v| compare( Functions::string(v), op, b ) }
854
+ b = Functions::string(b)
855
+ each_unnode(a).any? do |unnoded|
856
+ compare(Functions::string(unnoded), op, b)
857
+ end
845
858
  end
846
859
  else
847
860
  # If neither is nodeset,
@@ -851,34 +864,52 @@ module REXML
851
864
  # Else, convert to string
852
865
  # Else
853
866
  # Convert both to numbers and compare
854
- set1 = unnode(set1) if set1.is_a?(Array)
855
- set2 = unnode(set2) if set2.is_a?(Array)
856
- s1 = Functions.string(set1)
857
- s2 = Functions.string(set2)
858
- if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
859
- set1 = Functions::boolean( set1 )
860
- set2 = Functions::boolean( set2 )
867
+ compare(set1, op, set2)
868
+ end
869
+ end
870
+
871
+ def value_type(value)
872
+ case value
873
+ when true, false
874
+ :boolean
875
+ when Numeric
876
+ :number
877
+ when String
878
+ :string
879
+ else
880
+ raise "[BUG] Unexpected value type: <#{value.inspect}>"
881
+ end
882
+ end
883
+
884
+ def normalize_compare_values(a, operator, b)
885
+ a_type = value_type(a)
886
+ b_type = value_type(b)
887
+ case operator
888
+ when :eq, :neq
889
+ if a_type == :boolean or b_type == :boolean
890
+ a = Functions.boolean(a) unless a_type == :boolean
891
+ b = Functions.boolean(b) unless b_type == :boolean
892
+ elsif a_type == :number or b_type == :number
893
+ a = Functions.number(a) unless a_type == :number
894
+ b = Functions.number(b) unless b_type == :number
861
895
  else
862
- if op == :eq or op == :neq
863
- if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
864
- set1 = Functions::number( s1 )
865
- set2 = Functions::number( s2 )
866
- else
867
- set1 = Functions::string( set1 )
868
- set2 = Functions::string( set2 )
869
- end
870
- else
871
- set1 = Functions::number( set1 )
872
- set2 = Functions::number( set2 )
873
- end
896
+ a = Functions.string(a) unless a_type == :string
897
+ b = Functions.string(b) unless b_type == :string
874
898
  end
875
- return compare( set1, op, set2 )
899
+ when :lt, :lteq, :gt, :gteq
900
+ a = Functions.number(a) unless a_type == :number
901
+ b = Functions.number(b) unless b_type == :number
902
+ else
903
+ message = "[BUG] Unexpected compare operator: " +
904
+ "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
905
+ raise message
876
906
  end
877
- return false
907
+ [a, b]
878
908
  end
879
909
 
880
- def compare a, op, b
881
- case op
910
+ def compare(a, operator, b)
911
+ a, b = normalize_compare_values(a, operator, b)
912
+ case operator
882
913
  when :eq
883
914
  a == b
884
915
  when :neq
@@ -891,22 +922,27 @@ module REXML
891
922
  a > b
892
923
  when :gteq
893
924
  a >= b
894
- when :and
895
- a and b
896
- when :or
897
- a or b
898
925
  else
899
- false
926
+ message = "[BUG] Unexpected compare operator: " +
927
+ "<#{operator.inspect}>: <#{a.inspect}>: <#{b.inspect}>"
928
+ raise message
900
929
  end
901
930
  end
902
931
 
903
- def unnode(nodeset)
904
- nodeset.collect do |node|
932
+ def each_unnode(nodeset)
933
+ return to_enum(__method__, nodeset) unless block_given?
934
+ nodeset.each do |node|
905
935
  if node.is_a?(XPathNode)
906
936
  unnoded = node.raw_node
907
937
  else
908
938
  unnoded = node
909
939
  end
940
+ yield(unnoded)
941
+ end
942
+ end
943
+
944
+ def unnode(nodeset)
945
+ each_unnode(nodeset).collect do |unnoded|
910
946
  unnoded = yield(unnoded) if block_given?
911
947
  unnoded
912
948
  end
@@ -64,7 +64,6 @@ Gem::Specification.new do |spec|
64
64
  "lib/rexml/security.rb",
65
65
  "lib/rexml/source.rb",
66
66
  "lib/rexml/streamlistener.rb",
67
- "lib/rexml/syncenumerator.rb",
68
67
  "lib/rexml/text.rb",
69
68
  "lib/rexml/undefinednamespaceexception.rb",
70
69
  "lib/rexml/validation/relaxng.rb",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rexml
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-04 00:00:00.000000000 Z
11
+ date: 2019-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -92,7 +92,6 @@ files:
92
92
  - lib/rexml/security.rb
93
93
  - lib/rexml/source.rb
94
94
  - lib/rexml/streamlistener.rb
95
- - lib/rexml/syncenumerator.rb
96
95
  - lib/rexml/text.rb
97
96
  - lib/rexml/undefinednamespaceexception.rb
98
97
  - lib/rexml/validation/relaxng.rb
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: false
2
- module REXML
3
- class SyncEnumerator
4
- include Enumerable
5
-
6
- # Creates a new SyncEnumerator which enumerates rows of given
7
- # Enumerable objects.
8
- def initialize(*enums)
9
- @gens = enums
10
- @length = @gens.collect {|x| x.size }.max
11
- end
12
-
13
- # Returns the number of enumerated Enumerable objects, i.e. the size
14
- # of each row.
15
- def size
16
- @gens.size
17
- end
18
-
19
- # Returns the number of enumerated Enumerable objects, i.e. the size
20
- # of each row.
21
- def length
22
- @gens.length
23
- end
24
-
25
- # Enumerates rows of the Enumerable objects.
26
- def each
27
- @length.times {|i|
28
- yield @gens.collect {|x| x[i]}
29
- }
30
- self
31
- end
32
- end
33
- end