rexml 3.2.5 → 3.2.6

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.

@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  require_relative "namespace"
3
3
  require_relative 'text'
4
4
 
@@ -13,9 +13,6 @@ module REXML
13
13
 
14
14
  # The element to which this attribute belongs
15
15
  attr_reader :element
16
- # The normalized value of this attribute. That is, the attribute with
17
- # entities intact.
18
- attr_writer :normalized
19
16
  PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
20
17
 
21
18
  NEEDS_A_SECOND_CHECK = /(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um
@@ -122,10 +119,13 @@ module REXML
122
119
  # b = Attribute.new( "ns:x", "y" )
123
120
  # b.to_string # -> "ns:x='y'"
124
121
  def to_string
122
+ value = to_s
125
123
  if @element and @element.context and @element.context[:attribute_quote] == :quote
126
- %Q^#@expanded_name="#{to_s().gsub(/"/, '&quot;')}"^
124
+ value = value.gsub('"', '&quot;') if value.include?('"')
125
+ %Q^#@expanded_name="#{value}"^
127
126
  else
128
- "#@expanded_name='#{to_s().gsub(/'/, '&apos;')}'"
127
+ value = value.gsub("'", '&apos;') if value.include?("'")
128
+ "#@expanded_name='#{value}'"
129
129
  end
130
130
  end
131
131
 
@@ -141,7 +141,6 @@ module REXML
141
141
  return @normalized if @normalized
142
142
 
143
143
  @normalized = Text::normalize( @unnormalized, doctype )
144
- @unnormalized = nil
145
144
  @normalized
146
145
  end
147
146
 
@@ -150,10 +149,16 @@ module REXML
150
149
  def value
151
150
  return @unnormalized if @unnormalized
152
151
  @unnormalized = Text::unnormalize( @normalized, doctype )
153
- @normalized = nil
154
152
  @unnormalized
155
153
  end
156
154
 
155
+ # The normalized value of this attribute. That is, the attribute with
156
+ # entities intact.
157
+ def normalized=(new_normalized)
158
+ @normalized = new_normalized
159
+ @unnormalized = nil
160
+ end
161
+
157
162
  # Returns a copy of this attribute
158
163
  def clone
159
164
  Attribute.new self
@@ -190,7 +195,7 @@ module REXML
190
195
  end
191
196
 
192
197
  def inspect
193
- rv = ""
198
+ rv = +""
194
199
  write( rv )
195
200
  rv
196
201
  end
@@ -69,7 +69,7 @@ module REXML
69
69
  # d.to_s # => "<root><foo>Foo</foo><bar>Bar</bar></root>"
70
70
  #
71
71
  # When argument +document+ is given, it must be an existing
72
- # document object, whose context and attributes (but not chidren)
72
+ # document object, whose context and attributes (but not children)
73
73
  # are cloned into the new document:
74
74
  #
75
75
  # d = REXML::Document.new(xml_string)
data/lib/rexml/element.rb CHANGED
@@ -989,7 +989,7 @@ module REXML
989
989
  # :call-seq:
990
990
  # has_text? -> true or false
991
991
  #
992
- # Returns +true if the element has one or more text noded,
992
+ # Returns +true+ if the element has one or more text noded,
993
993
  # +false+ otherwise:
994
994
  #
995
995
  # d = REXML::Document.new '<a><b/>text<c/></a>'
@@ -1006,7 +1006,7 @@ module REXML
1006
1006
  # text(xpath = nil) -> text_string or nil
1007
1007
  #
1008
1008
  # Returns the text string from the first text node child
1009
- # in a specified element, if it exists, # +nil+ otherwise.
1009
+ # in a specified element, if it exists, +nil+ otherwise.
1010
1010
  #
1011
1011
  # With no argument, returns the text from the first text node in +self+:
1012
1012
  #
@@ -1014,7 +1014,7 @@ module REXML
1014
1014
  # d.root.text.class # => String
1015
1015
  # d.root.text # => "some text "
1016
1016
  #
1017
- # With argument +xpath+, returns text from the the first text node
1017
+ # With argument +xpath+, returns text from the first text node
1018
1018
  # in the element that matches +xpath+:
1019
1019
  #
1020
1020
  # d.root.text(1) # => "this is bold!"
data/lib/rexml/entity.rb CHANGED
@@ -132,24 +132,34 @@ module REXML
132
132
  # then:
133
133
  # doctype.entity('yada').value #-> "nanoo bar nanoo"
134
134
  def value
135
- if @value
136
- matches = @value.scan(PEREFERENCE_RE)
137
- rv = @value.clone
138
- if @parent
139
- sum = 0
140
- matches.each do |entity_reference|
141
- entity_value = @parent.entity( entity_reference[0] )
142
- if sum + entity_value.bytesize > Security.entity_expansion_text_limit
143
- raise "entity expansion has grown too large"
144
- else
145
- sum += entity_value.bytesize
146
- end
147
- rv.gsub!( /%#{entity_reference.join};/um, entity_value )
135
+ @resolved_value ||= resolve_value
136
+ end
137
+
138
+ def parent=(other)
139
+ @resolved_value = nil
140
+ super
141
+ end
142
+
143
+ private
144
+ def resolve_value
145
+ return nil if @value.nil?
146
+ return @value unless @value.match?(PEREFERENCE_RE)
147
+
148
+ matches = @value.scan(PEREFERENCE_RE)
149
+ rv = @value.clone
150
+ if @parent
151
+ sum = 0
152
+ matches.each do |entity_reference|
153
+ entity_value = @parent.entity( entity_reference[0] )
154
+ if sum + entity_value.bytesize > Security.entity_expansion_text_limit
155
+ raise "entity expansion has grown too large"
156
+ else
157
+ sum += entity_value.bytesize
148
158
  end
159
+ rv.gsub!( /%#{entity_reference.join};/um, entity_value )
149
160
  end
150
- return rv
151
161
  end
152
- nil
162
+ rv
153
163
  end
154
164
  end
155
165
 
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  require_relative 'default'
3
3
 
4
4
  module REXML
@@ -58,7 +58,7 @@ module REXML
58
58
  skip = false
59
59
  if compact
60
60
  if node.children.inject(true) {|s,c| s & c.kind_of?(Text)}
61
- string = ""
61
+ string = +""
62
62
  old_level = @level
63
63
  @level = 0
64
64
  node.children.each { |child| write( child, string ) }
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
 
3
3
  require_relative 'xmltokens'
4
4
 
@@ -10,13 +10,17 @@ module REXML
10
10
  # The expanded name of the object, valid if name is set
11
11
  attr_accessor :prefix
12
12
  include XMLTokens
13
+ NAME_WITHOUT_NAMESPACE = /\A#{NCNAME_STR}\z/
13
14
  NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u
14
15
 
15
16
  # Sets the name and the expanded name
16
17
  def name=( name )
17
18
  @expanded_name = name
18
- case name
19
- when NAMESPLIT
19
+ if name.match?(NAME_WITHOUT_NAMESPACE)
20
+ @prefix = ""
21
+ @namespace = ""
22
+ @name = name
23
+ elsif name =~ NAMESPLIT
20
24
  if $1
21
25
  @prefix = $1
22
26
  else
@@ -24,7 +28,7 @@ module REXML
24
28
  @namespace = ""
25
29
  end
26
30
  @name = $2
27
- when ""
31
+ elsif name == ""
28
32
  @prefix = nil
29
33
  @namespace = nil
30
34
  @name = nil
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: false
2
+
2
3
  require_relative '../namespace'
3
4
  require_relative '../xmltokens'
4
5
 
@@ -38,108 +39,143 @@ module REXML
38
39
  parsed
39
40
  end
40
41
 
41
- def abbreviate( path )
42
- path = path.kind_of?(String) ? parse( path ) : path
43
- string = ""
44
- document = false
45
- while path.size > 0
46
- op = path.shift
42
+ def abbreviate(path_or_parsed)
43
+ if path_or_parsed.kind_of?(String)
44
+ parsed = parse(path_or_parsed)
45
+ else
46
+ parsed = path_or_parsed
47
+ end
48
+ components = []
49
+ component = nil
50
+ while parsed.size > 0
51
+ op = parsed.shift
47
52
  case op
48
53
  when :node
54
+ component << "node()"
49
55
  when :attribute
50
- string << "/" if string.size > 0
51
- string << "@"
56
+ component = "@"
57
+ components << component
52
58
  when :child
53
- string << "/" if string.size > 0
59
+ component = ""
60
+ components << component
54
61
  when :descendant_or_self
55
- string << "/"
62
+ next_op = parsed[0]
63
+ if next_op == :node
64
+ parsed.shift
65
+ component = ""
66
+ components << component
67
+ else
68
+ component = "descendant-or-self::"
69
+ components << component
70
+ end
56
71
  when :self
57
- string << "."
72
+ next_op = parsed[0]
73
+ if next_op == :node
74
+ parsed.shift
75
+ components << "."
76
+ else
77
+ component = "self::"
78
+ components << component
79
+ end
58
80
  when :parent
59
- string << ".."
81
+ next_op = parsed[0]
82
+ if next_op == :node
83
+ parsed.shift
84
+ components << ".."
85
+ else
86
+ component = "parent::"
87
+ components << component
88
+ end
60
89
  when :any
61
- string << "*"
90
+ component << "*"
62
91
  when :text
63
- string << "text()"
92
+ component << "text()"
64
93
  when :following, :following_sibling,
65
94
  :ancestor, :ancestor_or_self, :descendant,
66
95
  :namespace, :preceding, :preceding_sibling
67
- string << "/" unless string.size == 0
68
- string << op.to_s.tr("_", "-")
69
- string << "::"
96
+ component = op.to_s.tr("_", "-") << "::"
97
+ components << component
70
98
  when :qname
71
- prefix = path.shift
72
- name = path.shift
73
- string << prefix+":" if prefix.size > 0
74
- string << name
99
+ prefix = parsed.shift
100
+ name = parsed.shift
101
+ component << prefix+":" if prefix.size > 0
102
+ component << name
75
103
  when :predicate
76
- string << '['
77
- string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
78
- string << ']'
104
+ component << '['
105
+ component << predicate_to_path(parsed.shift) {|x| abbreviate(x)}
106
+ component << ']'
79
107
  when :document
80
- document = true
108
+ components << ""
81
109
  when :function
82
- string << path.shift
83
- string << "( "
84
- string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
85
- string << " )"
110
+ component << parsed.shift
111
+ component << "( "
112
+ component << predicate_to_path(parsed.shift[0]) {|x| abbreviate(x)}
113
+ component << " )"
86
114
  when :literal
87
- string << %Q{ "#{path.shift}" }
115
+ component << quote_literal(parsed.shift)
88
116
  else
89
- string << "/" unless string.size == 0
90
- string << "UNKNOWN("
91
- string << op.inspect
92
- string << ")"
117
+ component << "UNKNOWN("
118
+ component << op.inspect
119
+ component << ")"
93
120
  end
94
121
  end
95
- string = "/"+string if document
96
- return string
122
+ case components
123
+ when [""]
124
+ "/"
125
+ when ["", ""]
126
+ "//"
127
+ else
128
+ components.join("/")
129
+ end
97
130
  end
98
131
 
99
- def expand( path )
100
- path = path.kind_of?(String) ? parse( path ) : path
101
- string = ""
132
+ def expand(path_or_parsed)
133
+ if path_or_parsed.kind_of?(String)
134
+ parsed = parse(path_or_parsed)
135
+ else
136
+ parsed = path_or_parsed
137
+ end
138
+ path = ""
102
139
  document = false
103
- while path.size > 0
104
- op = path.shift
140
+ while parsed.size > 0
141
+ op = parsed.shift
105
142
  case op
106
143
  when :node
107
- string << "node()"
144
+ path << "node()"
108
145
  when :attribute, :child, :following, :following_sibling,
109
146
  :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
110
147
  :namespace, :preceding, :preceding_sibling, :self, :parent
111
- string << "/" unless string.size == 0
112
- string << op.to_s.tr("_", "-")
113
- string << "::"
148
+ path << "/" unless path.size == 0
149
+ path << op.to_s.tr("_", "-")
150
+ path << "::"
114
151
  when :any
115
- string << "*"
152
+ path << "*"
116
153
  when :qname
117
- prefix = path.shift
118
- name = path.shift
119
- string << prefix+":" if prefix.size > 0
120
- string << name
154
+ prefix = parsed.shift
155
+ name = parsed.shift
156
+ path << prefix+":" if prefix.size > 0
157
+ path << name
121
158
  when :predicate
122
- string << '['
123
- string << predicate_to_string( path.shift ) { |x| expand(x) }
124
- string << ']'
159
+ path << '['
160
+ path << predicate_to_path( parsed.shift ) { |x| expand(x) }
161
+ path << ']'
125
162
  when :document
126
163
  document = true
127
164
  else
128
- string << "/" unless string.size == 0
129
- string << "UNKNOWN("
130
- string << op.inspect
131
- string << ")"
165
+ path << "UNKNOWN("
166
+ path << op.inspect
167
+ path << ")"
132
168
  end
133
169
  end
134
- string = "/"+string if document
135
- return string
170
+ path = "/"+path if document
171
+ path
136
172
  end
137
173
 
138
- def predicate_to_string( path, &block )
139
- string = ""
140
- case path[0]
174
+ def predicate_to_path(parsed, &block)
175
+ path = ""
176
+ case parsed[0]
141
177
  when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
142
- op = path.shift
178
+ op = parsed.shift
143
179
  case op
144
180
  when :eq
145
181
  op = "="
@@ -156,36 +192,50 @@ module REXML
156
192
  when :union
157
193
  op = "|"
158
194
  end
159
- left = predicate_to_string( path.shift, &block )
160
- right = predicate_to_string( path.shift, &block )
161
- string << " "
162
- string << left
163
- string << " "
164
- string << op.to_s
165
- string << " "
166
- string << right
167
- string << " "
195
+ left = predicate_to_path( parsed.shift, &block )
196
+ right = predicate_to_path( parsed.shift, &block )
197
+ path << left
198
+ path << " "
199
+ path << op.to_s
200
+ path << " "
201
+ path << right
168
202
  when :function
169
- path.shift
170
- name = path.shift
171
- string << name
172
- string << "( "
173
- string << predicate_to_string( path.shift, &block )
174
- string << " )"
203
+ parsed.shift
204
+ name = parsed.shift
205
+ path << name
206
+ path << "("
207
+ parsed.shift.each_with_index do |argument, i|
208
+ path << ", " if i > 0
209
+ path << predicate_to_path(argument, &block)
210
+ end
211
+ path << ")"
175
212
  when :literal
176
- path.shift
177
- string << " "
178
- string << path.shift.inspect
179
- string << " "
213
+ parsed.shift
214
+ path << quote_literal(parsed.shift)
180
215
  else
181
- string << " "
182
- string << yield( path )
183
- string << " "
216
+ path << yield( parsed )
184
217
  end
185
- return string.squeeze(" ")
218
+ return path.squeeze(" ")
186
219
  end
220
+ # For backward compatibility
221
+ alias_method :preciate_to_string, :predicate_to_path
187
222
 
188
223
  private
224
+ def quote_literal( literal )
225
+ case literal
226
+ when String
227
+ # XPath 1.0 does not support escape characters.
228
+ # Assumes literal does not contain both single and double quotes.
229
+ if literal.include?("'")
230
+ "\"#{literal}\""
231
+ else
232
+ "'#{literal}'"
233
+ end
234
+ else
235
+ literal.inspect
236
+ end
237
+ end
238
+
189
239
  #LocationPath
190
240
  # | RelativeLocationPath
191
241
  # | '/' RelativeLocationPath?
data/lib/rexml/rexml.rb CHANGED
@@ -26,10 +26,12 @@
26
26
  # - REXML::Document.
27
27
  # - REXML::Element.
28
28
  #
29
+ # There's also an {REXML tutorial}[doc/rexml/tutorial_rdoc.html].
30
+ #
29
31
  module REXML
30
32
  COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
31
33
  DATE = "2008/019"
32
- VERSION = "3.2.5"
34
+ VERSION = "3.2.6"
33
35
  REVISION = ""
34
36
 
35
37
  Copyright = COPYRIGHT
data/lib/rexml/text.rb CHANGED
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  require_relative 'security'
3
3
  require_relative 'entity'
4
4
  require_relative 'doctype'
@@ -131,7 +131,7 @@ module REXML
131
131
  def Text.check string, pattern, doctype
132
132
 
133
133
  # illegal anywhere
134
- if string !~ VALID_XML_CHARS
134
+ if !string.match?(VALID_XML_CHARS)
135
135
  if String.method_defined? :encode
136
136
  string.chars.each do |c|
137
137
  case c.ord
@@ -371,7 +371,7 @@ module REXML
371
371
  copy = input.to_s
372
372
  # Doing it like this rather than in a loop improves the speed
373
373
  #copy = copy.gsub( EREFERENCE, '&amp;' )
374
- copy = copy.gsub( "&", "&amp;" )
374
+ copy = copy.gsub( "&", "&amp;" ) if copy.include?("&")
375
375
  if doctype
376
376
  # Replace all ampersands that aren't part of an entity
377
377
  doctype.entities.each_value do |entity|
@@ -382,7 +382,9 @@ module REXML
382
382
  else
383
383
  # Replace all ampersands that aren't part of an entity
384
384
  DocType::DEFAULT_ENTITIES.each_value do |entity|
385
- copy = copy.gsub(entity.value, "&#{entity.name};" )
385
+ if copy.include?(entity.value)
386
+ copy = copy.gsub(entity.value, "&#{entity.name};" )
387
+ end
386
388
  end
387
389
  end
388
390
  copy
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.5
4
+ version: 3.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-05 00:00:00.000000000 Z
11
+ date: 2023-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -73,6 +73,7 @@ extra_rdoc_files:
73
73
  - doc/rexml/tasks/tocs/master_toc.rdoc
74
74
  - doc/rexml/tasks/tocs/node_toc.rdoc
75
75
  - doc/rexml/tasks/tocs/parent_toc.rdoc
76
+ - doc/rexml/tutorial.rdoc
76
77
  files:
77
78
  - LICENSE.txt
78
79
  - NEWS.md
@@ -89,6 +90,7 @@ files:
89
90
  - doc/rexml/tasks/tocs/master_toc.rdoc
90
91
  - doc/rexml/tasks/tocs/node_toc.rdoc
91
92
  - doc/rexml/tasks/tocs/parent_toc.rdoc
93
+ - doc/rexml/tutorial.rdoc
92
94
  - lib/rexml.rb
93
95
  - lib/rexml/attlistdecl.rb
94
96
  - lib/rexml/attribute.rb
@@ -143,7 +145,7 @@ homepage: https://github.com/ruby/rexml
143
145
  licenses:
144
146
  - BSD-2-Clause
145
147
  metadata: {}
146
- post_install_message:
148
+ post_install_message:
147
149
  rdoc_options:
148
150
  - "--main"
149
151
  - README.md
@@ -153,15 +155,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
155
  requirements:
154
156
  - - ">="
155
157
  - !ruby/object:Gem::Version
156
- version: '0'
158
+ version: 2.5.0
157
159
  required_rubygems_version: !ruby/object:Gem::Requirement
158
160
  requirements:
159
161
  - - ">="
160
162
  - !ruby/object:Gem::Version
161
163
  version: '0'
162
164
  requirements: []
163
- rubygems_version: 3.2.3
164
- signing_key:
165
+ rubygems_version: 3.5.0.dev
166
+ signing_key:
165
167
  specification_version: 4
166
168
  summary: An XML toolkit for Ruby
167
169
  test_files: []