rexml 3.2.3 → 3.2.8

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
data/lib/rexml/doctype.rb CHANGED
@@ -7,6 +7,44 @@ require_relative 'attlistdecl'
7
7
  require_relative 'xmltokens'
8
8
 
9
9
  module REXML
10
+ class ReferenceWriter
11
+ def initialize(id_type,
12
+ public_id_literal,
13
+ system_literal,
14
+ context=nil)
15
+ @id_type = id_type
16
+ @public_id_literal = public_id_literal
17
+ @system_literal = system_literal
18
+ if context and context[:prologue_quote] == :apostrophe
19
+ @default_quote = "'"
20
+ else
21
+ @default_quote = "\""
22
+ end
23
+ end
24
+
25
+ def write(output)
26
+ output << " #{@id_type}"
27
+ if @public_id_literal
28
+ if @public_id_literal.include?("'")
29
+ quote = "\""
30
+ else
31
+ quote = @default_quote
32
+ end
33
+ output << " #{quote}#{@public_id_literal}#{quote}"
34
+ end
35
+ if @system_literal
36
+ if @system_literal.include?("'")
37
+ quote = "\""
38
+ elsif @system_literal.include?("\"")
39
+ quote = "'"
40
+ else
41
+ quote = @default_quote
42
+ end
43
+ output << " #{quote}#{@system_literal}#{quote}"
44
+ end
45
+ end
46
+ end
47
+
10
48
  # Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
11
49
  # ... >. DOCTYPES can be used to declare the DTD of a document, as well as
12
50
  # being used to declare entities used in the document.
@@ -50,6 +88,8 @@ module REXML
50
88
  super( parent )
51
89
  @name = first.name
52
90
  @external_id = first.external_id
91
+ @long_name = first.instance_variable_get(:@long_name)
92
+ @uri = first.instance_variable_get(:@uri)
53
93
  elsif first.kind_of? Array
54
94
  super( parent )
55
95
  @name = first[0]
@@ -108,19 +148,17 @@ module REXML
108
148
  # Ignored
109
149
  def write( output, indent=0, transitive=false, ie_hack=false )
110
150
  f = REXML::Formatters::Default.new
111
- c = context
112
- if c and c[:prologue_quote] == :apostrophe
113
- quote = "'"
114
- else
115
- quote = "\""
116
- end
117
151
  indent( output, indent )
118
152
  output << START
119
153
  output << ' '
120
154
  output << @name
121
- output << " #{@external_id}" if @external_id
122
- output << " #{quote}#{@long_name}#{quote}" if @long_name
123
- output << " #{quote}#{@uri}#{quote}" if @uri
155
+ if @external_id
156
+ reference_writer = ReferenceWriter.new(@external_id,
157
+ @long_name,
158
+ @uri,
159
+ context)
160
+ reference_writer.write(output)
161
+ end
124
162
  unless @children.empty?
125
163
  output << ' ['
126
164
  @children.each { |child|
@@ -159,7 +197,7 @@ module REXML
159
197
  when "SYSTEM"
160
198
  nil
161
199
  when "PUBLIC"
162
- strip_quotes(@long_name)
200
+ @long_name
163
201
  end
164
202
  end
165
203
 
@@ -169,9 +207,9 @@ module REXML
169
207
  def system
170
208
  case @external_id
171
209
  when "SYSTEM"
172
- strip_quotes(@long_name)
210
+ @long_name
173
211
  when "PUBLIC"
174
- @uri.kind_of?(String) ? strip_quotes(@uri) : nil
212
+ @uri.kind_of?(String) ? @uri : nil
175
213
  end
176
214
  end
177
215
 
@@ -193,15 +231,6 @@ module REXML
193
231
  notation_decl.name == name
194
232
  }
195
233
  end
196
-
197
- private
198
-
199
- # Method contributed by Henrik Martensson
200
- def strip_quotes(quoted_string)
201
- quoted_string =~ /^[\'\"].*[\'\"]$/ ?
202
- quoted_string[1, quoted_string.length-2] :
203
- quoted_string
204
- end
205
234
  end
206
235
 
207
236
  # We don't really handle any of these since we're not a validating
@@ -259,16 +288,11 @@ module REXML
259
288
  end
260
289
 
261
290
  def to_s
262
- c = nil
263
- c = parent.context if parent
264
- if c and c[:prologue_quote] == :apostrophe
265
- quote = "'"
266
- else
267
- quote = "\""
268
- end
269
- notation = "<!NOTATION #{@name} #{@middle}"
270
- notation << " #{quote}#{@public}#{quote}" if @public
271
- notation << " #{quote}#{@system}#{quote}" if @system
291
+ context = nil
292
+ context = parent.context if parent
293
+ notation = "<!NOTATION #{@name}"
294
+ reference_writer = ReferenceWriter.new(@middle, @public, @system, context)
295
+ reference_writer.write(notation)
272
296
  notation << ">"
273
297
  notation
274
298
  end
@@ -14,25 +14,81 @@ require_relative "parsers/streamparser"
14
14
  require_relative "parsers/treeparser"
15
15
 
16
16
  module REXML
17
- # Represents a full XML document, including PIs, a doctype, etc. A
18
- # Document has a single child that can be accessed by root().
19
- # Note that if you want to have an XML declaration written for a document
20
- # you create, you must add one; REXML documents do not write a default
21
- # declaration for you. See |DECLARATION| and |write|.
17
+ # Represents an XML document.
18
+ #
19
+ # A document may have:
20
+ #
21
+ # - A single child that may be accessed via method #root.
22
+ # - An XML declaration.
23
+ # - A document type.
24
+ # - Processing instructions.
25
+ #
26
+ # == In a Hurry?
27
+ #
28
+ # If you're somewhat familiar with XML
29
+ # and have a particular task in mind,
30
+ # you may want to see the
31
+ # {tasks pages}[../doc/rexml/tasks/tocs/master_toc_rdoc.html],
32
+ # and in particular, the
33
+ # {tasks page for documents}[../doc/rexml/tasks/tocs/document_toc_rdoc.html].
34
+ #
22
35
  class Document < Element
23
- # A convenient default XML declaration. If you want an XML declaration,
24
- # the easiest way to add one is mydoc << Document::DECLARATION
25
- # +DEPRECATED+
26
- # Use: mydoc << XMLDecl.default
36
+ # A convenient default XML declaration. Use:
37
+ #
38
+ # mydoc << XMLDecl.default
39
+ #
27
40
  DECLARATION = XMLDecl.default
28
41
 
29
- # Constructor
30
- # @param source if supplied, must be a Document, String, or IO.
31
- # Documents have their context and Element attributes cloned.
32
- # Strings are expected to be valid XML documents. IOs are expected
33
- # to be sources of valid XML documents.
34
- # @param context if supplied, contains the context of the document;
35
- # this should be a Hash.
42
+ # :call-seq:
43
+ # new(string = nil, context = {}) -> new_document
44
+ # new(io_stream = nil, context = {}) -> new_document
45
+ # new(document = nil, context = {}) -> new_document
46
+ #
47
+ # Returns a new \REXML::Document object.
48
+ #
49
+ # When no arguments are given,
50
+ # returns an empty document:
51
+ #
52
+ # d = REXML::Document.new
53
+ # d.to_s # => ""
54
+ #
55
+ # When argument +string+ is given, it must be a string
56
+ # containing a valid XML document:
57
+ #
58
+ # xml_string = '<root><foo>Foo</foo><bar>Bar</bar></root>'
59
+ # d = REXML::Document.new(xml_string)
60
+ # d.to_s # => "<root><foo>Foo</foo><bar>Bar</bar></root>"
61
+ #
62
+ # When argument +io_stream+ is given, it must be an \IO object
63
+ # that is opened for reading, and when read must return a valid XML document:
64
+ #
65
+ # File.write('t.xml', xml_string)
66
+ # d = File.open('t.xml', 'r') do |io|
67
+ # REXML::Document.new(io)
68
+ # end
69
+ # d.to_s # => "<root><foo>Foo</foo><bar>Bar</bar></root>"
70
+ #
71
+ # When argument +document+ is given, it must be an existing
72
+ # document object, whose context and attributes (but not children)
73
+ # are cloned into the new document:
74
+ #
75
+ # d = REXML::Document.new(xml_string)
76
+ # d.children # => [<root> ... </>]
77
+ # d.context = {raw: :all, compress_whitespace: :all}
78
+ # d.add_attributes({'bar' => 0, 'baz' => 1})
79
+ # d1 = REXML::Document.new(d)
80
+ # d1.children # => []
81
+ # d1.context # => {:raw=>:all, :compress_whitespace=>:all}
82
+ # d1.attributes # => {"bar"=>bar='0', "baz"=>baz='1'}
83
+ #
84
+ # When argument +context+ is given, it must be a hash
85
+ # containing context entries for the document;
86
+ # see {Element Context}[../doc/rexml/context_rdoc.html]:
87
+ #
88
+ # context = {raw: :all, compress_whitespace: :all}
89
+ # d = REXML::Document.new(xml_string, context)
90
+ # d.context # => {:raw=>:all, :compress_whitespace=>:all}
91
+ #
36
92
  def initialize( source = nil, context = {} )
37
93
  @entity_expansion_count = 0
38
94
  super()
@@ -46,26 +102,71 @@ module REXML
46
102
  end
47
103
  end
48
104
 
105
+ # :call-seq:
106
+ # node_type -> :document
107
+ #
108
+ # Returns the symbol +:document+.
109
+ #
49
110
  def node_type
50
111
  :document
51
112
  end
52
113
 
53
- # Should be obvious
114
+ # :call-seq:
115
+ # clone -> new_document
116
+ #
117
+ # Returns the new document resulting from executing
118
+ # <tt>Document.new(self)</tt>. See Document.new.
119
+ #
54
120
  def clone
55
121
  Document.new self
56
122
  end
57
123
 
58
- # According to the XML spec, a root node has no expanded name
124
+ # :call-seq:
125
+ # expanded_name -> empty_string
126
+ #
127
+ # Returns an empty string.
128
+ #
59
129
  def expanded_name
60
130
  ''
61
131
  #d = doc_type
62
132
  #d ? d.name : "UNDEFINED"
63
133
  end
64
-
65
134
  alias :name :expanded_name
66
135
 
67
- # We override this, because XMLDecls and DocTypes must go at the start
68
- # of the document
136
+ # :call-seq:
137
+ # add(xml_decl) -> self
138
+ # add(doc_type) -> self
139
+ # add(object) -> self
140
+ #
141
+ # Adds an object to the document; returns +self+.
142
+ #
143
+ # When argument +xml_decl+ is given,
144
+ # it must be an REXML::XMLDecl object,
145
+ # which becomes the XML declaration for the document,
146
+ # replacing the previous XML declaration if any:
147
+ #
148
+ # d = REXML::Document.new
149
+ # d.xml_decl.to_s # => ""
150
+ # d.add(REXML::XMLDecl.new('2.0'))
151
+ # d.xml_decl.to_s # => "<?xml version='2.0'?>"
152
+ #
153
+ # When argument +doc_type+ is given,
154
+ # it must be an REXML::DocType object,
155
+ # which becomes the document type for the document,
156
+ # replacing the previous document type, if any:
157
+ #
158
+ # d = REXML::Document.new
159
+ # d.doctype.to_s # => ""
160
+ # d.add(REXML::DocType.new('foo'))
161
+ # d.doctype.to_s # => "<!DOCTYPE foo>"
162
+ #
163
+ # When argument +object+ (not an REXML::XMLDecl or REXML::DocType object)
164
+ # is given it is added as the last child:
165
+ #
166
+ # d = REXML::Document.new
167
+ # d.add(REXML::Element.new('foo'))
168
+ # d.to_s # => "<foo/>"
169
+ #
69
170
  def add( child )
70
171
  if child.kind_of? XMLDecl
71
172
  if @children[0].kind_of? XMLDecl
@@ -99,49 +200,108 @@ module REXML
99
200
  end
100
201
  alias :<< :add
101
202
 
203
+ # :call-seq:
204
+ # add_element(name_or_element = nil, attributes = nil) -> new_element
205
+ #
206
+ # Adds an element to the document by calling REXML::Element.add_element:
207
+ #
208
+ # REXML::Element.add_element(name_or_element, attributes)
102
209
  def add_element(arg=nil, arg2=nil)
103
210
  rv = super
104
211
  raise "attempted adding second root element to document" if @elements.size > 1
105
212
  rv
106
213
  end
107
214
 
108
- # @return the root Element of the document, or nil if this document
109
- # has no children.
215
+ # :call-seq:
216
+ # root -> root_element or nil
217
+ #
218
+ # Returns the root element of the document, if it exists, otherwise +nil+:
219
+ #
220
+ # d = REXML::Document.new('<root></root>')
221
+ # d.root # => <root/>
222
+ # d = REXML::Document.new('')
223
+ # d.root # => nil
224
+ #
110
225
  def root
111
226
  elements[1]
112
227
  #self
113
228
  #@children.find { |item| item.kind_of? Element }
114
229
  end
115
230
 
116
- # @return the DocType child of the document, if one exists,
117
- # and nil otherwise.
231
+ # :call-seq:
232
+ # doctype -> doc_type or nil
233
+ #
234
+ # Returns the DocType object for the document, if it exists, otherwise +nil+:
235
+ #
236
+ # d = REXML::Document.new('<!DOCTYPE document SYSTEM "subjects.dtd">')
237
+ # d.doctype.class # => REXML::DocType
238
+ # d = REXML::Document.new('')
239
+ # d.doctype.class # => nil
240
+ #
118
241
  def doctype
119
242
  @children.find { |item| item.kind_of? DocType }
120
243
  end
121
244
 
122
- # @return the XMLDecl of this document; if no XMLDecl has been
123
- # set, the default declaration is returned.
245
+ # :call-seq:
246
+ # xml_decl -> xml_decl
247
+ #
248
+ # Returns the XMLDecl object for the document, if it exists,
249
+ # otherwise the default XMLDecl object:
250
+ #
251
+ # d = REXML::Document.new('<?xml version="1.0" encoding="UTF-8"?>')
252
+ # d.xml_decl.class # => REXML::XMLDecl
253
+ # d.xml_decl.to_s # => "<?xml version='1.0' encoding='UTF-8'?>"
254
+ # d = REXML::Document.new('')
255
+ # d.xml_decl.class # => REXML::XMLDecl
256
+ # d.xml_decl.to_s # => ""
257
+ #
124
258
  def xml_decl
125
259
  rv = @children[0]
126
260
  return rv if rv.kind_of? XMLDecl
127
261
  @children.unshift(XMLDecl.default)[0]
128
262
  end
129
263
 
130
- # @return the XMLDecl version of this document as a String.
131
- # If no XMLDecl has been set, returns the default version.
264
+ # :call-seq:
265
+ # version -> version_string
266
+ #
267
+ # Returns the XMLDecl version of this document as a string,
268
+ # if it has been set, otherwise the default version:
269
+ #
270
+ # d = REXML::Document.new('<?xml version="2.0" encoding="UTF-8"?>')
271
+ # d.version # => "2.0"
272
+ # d = REXML::Document.new('')
273
+ # d.version # => "1.0"
274
+ #
132
275
  def version
133
276
  xml_decl().version
134
277
  end
135
278
 
136
- # @return the XMLDecl encoding of this document as an
137
- # Encoding object.
138
- # If no XMLDecl has been set, returns the default encoding.
279
+ # :call-seq:
280
+ # encoding -> encoding_string
281
+ #
282
+ # Returns the XMLDecl encoding of the document,
283
+ # if it has been set, otherwise the default encoding:
284
+ #
285
+ # d = REXML::Document.new('<?xml version="1.0" encoding="UTF-16"?>')
286
+ # d.encoding # => "UTF-16"
287
+ # d = REXML::Document.new('')
288
+ # d.encoding # => "UTF-8"
289
+ #
139
290
  def encoding
140
291
  xml_decl().encoding
141
292
  end
142
293
 
143
- # @return the XMLDecl standalone value of this document as a String.
144
- # If no XMLDecl has been set, returns the default setting.
294
+ # :call-seq:
295
+ # stand_alone?
296
+ #
297
+ # Returns the XMLDecl standalone value of the document as a string,
298
+ # if it has been set, otherwise the default standalone value:
299
+ #
300
+ # d = REXML::Document.new('<?xml standalone="yes"?>')
301
+ # d.stand_alone? # => "yes"
302
+ # d = REXML::Document.new('')
303
+ # d.stand_alone? # => nil
304
+ #
145
305
  def stand_alone?
146
306
  xml_decl().stand_alone?
147
307
  end