rexml 3.2.5 → 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.
- checksums.yaml +4 -4
- data/NEWS.md +166 -2
- data/README.md +10 -1
- data/doc/rexml/tasks/rdoc/element.rdoc +2 -2
- data/doc/rexml/tutorial.rdoc +1358 -0
- data/lib/rexml/attribute.rb +14 -9
- data/lib/rexml/document.rb +1 -1
- data/lib/rexml/element.rb +3 -3
- data/lib/rexml/entity.rb +25 -15
- data/lib/rexml/formatters/pretty.rb +2 -2
- data/lib/rexml/functions.rb +1 -2
- data/lib/rexml/namespace.rb +8 -4
- data/lib/rexml/parseexception.rb +1 -0
- data/lib/rexml/parsers/baseparser.rb +229 -230
- data/lib/rexml/parsers/xpathparser.rb +136 -86
- data/lib/rexml/rexml.rb +3 -1
- data/lib/rexml/source.rb +72 -99
- data/lib/rexml/text.rb +6 -4
- data/lib/rexml/xpath_parser.rb +7 -3
- metadata +11 -40
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
require_relative '../parseexception'
|
3
3
|
require_relative '../undefinednamespaceexception'
|
4
4
|
require_relative '../source'
|
@@ -96,7 +96,7 @@ module REXML
|
|
96
96
|
ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
|
97
97
|
PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
|
98
98
|
GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
|
99
|
-
ENTITYDECL = /\s*(?:#{GEDECL})
|
99
|
+
ENTITYDECL = /\s*(?:#{GEDECL})|\s*(?:#{PEDECL})/um
|
100
100
|
|
101
101
|
NOTATIONDECL_START = /\A\s*<!NOTATION/um
|
102
102
|
EXTERNAL_ID_PUBLIC = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}\s*/um
|
@@ -112,6 +112,19 @@ module REXML
|
|
112
112
|
"apos" => [/'/, "'", "'", /'/]
|
113
113
|
}
|
114
114
|
|
115
|
+
module Private
|
116
|
+
INSTRUCTION_END = /#{NAME}(\s+.*?)?\?>/um
|
117
|
+
TAG_PATTERN = /((?>#{QNAME_STR}))\s*/um
|
118
|
+
CLOSE_PATTERN = /(#{QNAME_STR})\s*>/um
|
119
|
+
ATTLISTDECL_END = /\s+#{NAME}(?:#{ATTDEF})*\s*>/um
|
120
|
+
NAME_PATTERN = /\s*#{NAME}/um
|
121
|
+
GEDECL_PATTERN = "\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
|
122
|
+
PEDECL_PATTERN = "\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
|
123
|
+
ENTITYDECL_PATTERN = /(?:#{GEDECL_PATTERN})|(?:#{PEDECL_PATTERN})/um
|
124
|
+
end
|
125
|
+
private_constant :Private
|
126
|
+
include Private
|
127
|
+
|
115
128
|
def initialize( source )
|
116
129
|
self.stream = source
|
117
130
|
@listeners = []
|
@@ -196,181 +209,180 @@ module REXML
|
|
196
209
|
return @stack.shift if @stack.size > 0
|
197
210
|
#STDERR.puts @source.encoding
|
198
211
|
#STDERR.puts "BUFFER = #{@source.buffer.inspect}"
|
212
|
+
|
213
|
+
@source.ensure_buffer
|
199
214
|
if @document_status == nil
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
if encoding.nil? and /\AUTF-16(?:BE|LE)\z/i =~ @source.encoding
|
217
|
-
encoding = "UTF-16"
|
218
|
-
end
|
219
|
-
standalone = STANDALONE.match(results)
|
220
|
-
standalone = standalone[1] unless standalone.nil?
|
221
|
-
return [ :xmldecl, version, encoding, standalone ]
|
222
|
-
when INSTRUCTION_START
|
223
|
-
return process_instruction
|
224
|
-
when DOCTYPE_START
|
225
|
-
base_error_message = "Malformed DOCTYPE"
|
226
|
-
@source.match(DOCTYPE_START, true)
|
227
|
-
@nsstack.unshift(curr_ns=Set.new)
|
228
|
-
name = parse_name(base_error_message)
|
229
|
-
if @source.match(/\A\s*\[/um, true)
|
230
|
-
id = [nil, nil, nil]
|
231
|
-
@document_status = :in_doctype
|
232
|
-
elsif @source.match(/\A\s*>/um, true)
|
233
|
-
id = [nil, nil, nil]
|
234
|
-
@document_status = :after_doctype
|
235
|
-
else
|
236
|
-
id = parse_id(base_error_message,
|
237
|
-
accept_external_id: true,
|
238
|
-
accept_public_id: false)
|
239
|
-
if id[0] == "SYSTEM"
|
240
|
-
# For backward compatibility
|
241
|
-
id[1], id[2] = id[2], nil
|
215
|
+
start_position = @source.position
|
216
|
+
if @source.match("<?", true)
|
217
|
+
return process_instruction(start_position)
|
218
|
+
elsif @source.match("<!", true)
|
219
|
+
if @source.match("--", true)
|
220
|
+
return [ :comment, @source.match(/(.*?)-->/um, true)[1] ]
|
221
|
+
elsif @source.match("DOCTYPE", true)
|
222
|
+
base_error_message = "Malformed DOCTYPE"
|
223
|
+
unless @source.match(/\s+/um, true)
|
224
|
+
if @source.match(">")
|
225
|
+
message = "#{base_error_message}: name is missing"
|
226
|
+
else
|
227
|
+
message = "#{base_error_message}: invalid name"
|
228
|
+
end
|
229
|
+
@source.position = start_position
|
230
|
+
raise REXML::ParseException.new(message, @source)
|
242
231
|
end
|
243
|
-
|
232
|
+
@nsstack.unshift(curr_ns=Set.new)
|
233
|
+
name = parse_name(base_error_message)
|
234
|
+
if @source.match(/\s*\[/um, true)
|
235
|
+
id = [nil, nil, nil]
|
244
236
|
@document_status = :in_doctype
|
245
|
-
elsif @source.match(/\
|
237
|
+
elsif @source.match(/\s*>/um, true)
|
238
|
+
id = [nil, nil, nil]
|
246
239
|
@document_status = :after_doctype
|
240
|
+
@source.ensure_buffer
|
247
241
|
else
|
248
|
-
|
249
|
-
|
242
|
+
id = parse_id(base_error_message,
|
243
|
+
accept_external_id: true,
|
244
|
+
accept_public_id: false)
|
245
|
+
if id[0] == "SYSTEM"
|
246
|
+
# For backward compatibility
|
247
|
+
id[1], id[2] = id[2], nil
|
248
|
+
end
|
249
|
+
if @source.match(/\s*\[/um, true)
|
250
|
+
@document_status = :in_doctype
|
251
|
+
elsif @source.match(/\s*>/um, true)
|
252
|
+
@document_status = :after_doctype
|
253
|
+
@source.ensure_buffer
|
254
|
+
else
|
255
|
+
message = "#{base_error_message}: garbage after external ID"
|
256
|
+
raise REXML::ParseException.new(message, @source)
|
257
|
+
end
|
250
258
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
@document_status = :after_doctype
|
261
|
-
if @source.encoding == "UTF-8"
|
262
|
-
@source.buffer.force_encoding(::Encoding::UTF_8)
|
259
|
+
args = [:start_doctype, name, *id]
|
260
|
+
if @document_status == :after_doctype
|
261
|
+
@source.match(/\s*/um, true)
|
262
|
+
@stack << [ :end_doctype ]
|
263
|
+
end
|
264
|
+
return args
|
265
|
+
else
|
266
|
+
message = "Invalid XML"
|
267
|
+
raise REXML::ParseException.new(message, @source)
|
263
268
|
end
|
264
269
|
end
|
265
270
|
end
|
266
271
|
if @document_status == :in_doctype
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if match[1] == '%'
|
281
|
-
ref = true
|
282
|
-
match.delete_at 1
|
283
|
-
end
|
284
|
-
# Now we have to sort out what kind of entity reference this is
|
285
|
-
if match[2] == 'SYSTEM'
|
286
|
-
# External reference
|
287
|
-
match[3] = match[3][1..-2] # PUBID
|
288
|
-
match.delete_at(4) if match.size > 4 # Chop out NDATA decl
|
289
|
-
# match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
|
290
|
-
elsif match[2] == 'PUBLIC'
|
291
|
-
# External reference
|
292
|
-
match[3] = match[3][1..-2] # PUBID
|
293
|
-
match[4] = match[4][1..-2] # HREF
|
294
|
-
match.delete_at(5) if match.size > 5 # Chop out NDATA decl
|
295
|
-
# match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ]
|
296
|
-
else
|
297
|
-
match[2] = match[2][1..-2]
|
298
|
-
match.pop if match.size == 4
|
299
|
-
# match is [ :entity, name, value ]
|
300
|
-
end
|
301
|
-
match << '%' if ref
|
302
|
-
return match
|
303
|
-
when ATTLISTDECL_START
|
304
|
-
md = @source.match( ATTLISTDECL_PATTERN, true )
|
305
|
-
raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
|
306
|
-
element = md[1]
|
307
|
-
contents = md[0]
|
308
|
-
|
309
|
-
pairs = {}
|
310
|
-
values = md[0].scan( ATTDEF_RE )
|
311
|
-
values.each do |attdef|
|
312
|
-
unless attdef[3] == "#IMPLIED"
|
313
|
-
attdef.compact!
|
314
|
-
val = attdef[3]
|
315
|
-
val = attdef[4] if val == "#FIXED "
|
316
|
-
pairs[attdef[0]] = val
|
317
|
-
if attdef[0] =~ /^xmlns:(.*)/
|
318
|
-
@nsstack[0] << $1
|
319
|
-
end
|
272
|
+
@source.match(/\s*/um, true) # skip spaces
|
273
|
+
start_position = @source.position
|
274
|
+
if @source.match("<!", true)
|
275
|
+
if @source.match("ELEMENT", true)
|
276
|
+
md = @source.match(/(.*?)>/um, true)
|
277
|
+
raise REXML::ParseException.new( "Bad ELEMENT declaration!", @source ) if md.nil?
|
278
|
+
return [ :elementdecl, "<!ELEMENT" + md[1] ]
|
279
|
+
elsif @source.match("ENTITY", true)
|
280
|
+
match = [:entitydecl, *@source.match(ENTITYDECL_PATTERN, true).captures.compact]
|
281
|
+
ref = false
|
282
|
+
if match[1] == '%'
|
283
|
+
ref = true
|
284
|
+
match.delete_at 1
|
320
285
|
end
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
286
|
+
# Now we have to sort out what kind of entity reference this is
|
287
|
+
if match[2] == 'SYSTEM'
|
288
|
+
# External reference
|
289
|
+
match[3] = match[3][1..-2] # PUBID
|
290
|
+
match.delete_at(4) if match.size > 4 # Chop out NDATA decl
|
291
|
+
# match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
|
292
|
+
elsif match[2] == 'PUBLIC'
|
293
|
+
# External reference
|
294
|
+
match[3] = match[3][1..-2] # PUBID
|
295
|
+
match[4] = match[4][1..-2] # HREF
|
296
|
+
match.delete_at(5) if match.size > 5 # Chop out NDATA decl
|
297
|
+
# match is [ :entity, name, PUBLIC, pubid, href(, ndata)? ]
|
328
298
|
else
|
329
|
-
|
299
|
+
match[2] = match[2][1..-2]
|
300
|
+
match.pop if match.size == 4
|
301
|
+
# match is [ :entity, name, value ]
|
330
302
|
end
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
303
|
+
match << '%' if ref
|
304
|
+
return match
|
305
|
+
elsif @source.match("ATTLIST", true)
|
306
|
+
md = @source.match(ATTLISTDECL_END, true)
|
307
|
+
raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
|
308
|
+
element = md[1]
|
309
|
+
contents = md[0]
|
310
|
+
|
311
|
+
pairs = {}
|
312
|
+
values = md[0].scan( ATTDEF_RE )
|
313
|
+
values.each do |attdef|
|
314
|
+
unless attdef[3] == "#IMPLIED"
|
315
|
+
attdef.compact!
|
316
|
+
val = attdef[3]
|
317
|
+
val = attdef[4] if val == "#FIXED "
|
318
|
+
pairs[attdef[0]] = val
|
319
|
+
if attdef[0] =~ /^xmlns:(.*)/
|
320
|
+
@nsstack[0] << $1
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
return [ :attlistdecl, element, pairs, contents ]
|
325
|
+
elsif @source.match("NOTATION", true)
|
326
|
+
base_error_message = "Malformed notation declaration"
|
327
|
+
unless @source.match(/\s+/um, true)
|
328
|
+
if @source.match(">")
|
329
|
+
message = "#{base_error_message}: name is missing"
|
330
|
+
else
|
331
|
+
message = "#{base_error_message}: invalid name"
|
332
|
+
end
|
333
|
+
@source.position = start_position
|
334
|
+
raise REXML::ParseException.new(message, @source)
|
335
|
+
end
|
336
|
+
name = parse_name(base_error_message)
|
337
|
+
id = parse_id(base_error_message,
|
338
|
+
accept_external_id: true,
|
339
|
+
accept_public_id: true)
|
340
|
+
unless @source.match(/\s*>/um, true)
|
341
|
+
message = "#{base_error_message}: garbage before end >"
|
342
|
+
raise REXML::ParseException.new(message, @source)
|
343
|
+
end
|
344
|
+
return [:notationdecl, name, *id]
|
345
|
+
elsif md = @source.match(/--(.*?)-->/um, true)
|
346
|
+
case md[1]
|
347
|
+
when /--/, /-\z/
|
348
|
+
raise REXML::ParseException.new("Malformed comment", @source)
|
349
|
+
end
|
350
|
+
return [ :comment, md[1] ] if md
|
340
351
|
end
|
341
|
-
|
342
|
-
|
352
|
+
elsif match = @source.match(/(%.*?;)\s*/um, true)
|
353
|
+
return [ :externalentity, match[1] ]
|
354
|
+
elsif @source.match(/\]\s*>/um, true)
|
343
355
|
@document_status = :after_doctype
|
344
|
-
@source.match( DOCTYPE_END, true )
|
345
356
|
return [ :end_doctype ]
|
346
357
|
end
|
347
358
|
end
|
348
359
|
if @document_status == :after_doctype
|
349
|
-
@source.match(/\
|
360
|
+
@source.match(/\s*/um, true)
|
350
361
|
end
|
351
362
|
begin
|
352
|
-
|
353
|
-
if @source.
|
354
|
-
if @source.
|
363
|
+
start_position = @source.position
|
364
|
+
if @source.match("<", true)
|
365
|
+
if @source.match("/", true)
|
355
366
|
@nsstack.shift
|
356
367
|
last_tag = @tags.pop
|
357
|
-
md = @source.match(
|
368
|
+
md = @source.match(CLOSE_PATTERN, true)
|
358
369
|
if md and !last_tag
|
359
370
|
message = "Unexpected top-level end tag (got '#{md[1]}')"
|
360
371
|
raise REXML::ParseException.new(message, @source)
|
361
372
|
end
|
362
373
|
if md.nil? or last_tag != md[1]
|
363
374
|
message = "Missing end tag for '#{last_tag}'"
|
364
|
-
message
|
375
|
+
message += " (got '#{md[1]}')" if md
|
376
|
+
@source.position = start_position if md.nil?
|
365
377
|
raise REXML::ParseException.new(message, @source)
|
366
378
|
end
|
367
379
|
return [ :end_element, last_tag ]
|
368
|
-
elsif @source.
|
369
|
-
md = @source.match(
|
380
|
+
elsif @source.match("!", true)
|
381
|
+
md = @source.match(/([^>]*>)/um)
|
370
382
|
#STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
|
371
383
|
raise REXML::ParseException.new("Malformed node", @source) unless md
|
372
|
-
if md[0][
|
373
|
-
md = @source.match(
|
384
|
+
if md[0][0] == ?-
|
385
|
+
md = @source.match(/--(.*?)-->/um, true)
|
374
386
|
|
375
387
|
case md[1]
|
376
388
|
when /--/, /-\z/
|
@@ -379,19 +391,21 @@ module REXML
|
|
379
391
|
|
380
392
|
return [ :comment, md[1] ] if md
|
381
393
|
else
|
382
|
-
md = @source.match(
|
394
|
+
md = @source.match(/\[CDATA\[(.*?)\]\]>/um, true)
|
383
395
|
return [ :cdata, md[1] ] if md
|
384
396
|
end
|
385
397
|
raise REXML::ParseException.new( "Declarations can only occur "+
|
386
398
|
"in the doctype declaration.", @source)
|
387
|
-
elsif @source.
|
388
|
-
return process_instruction
|
399
|
+
elsif @source.match("?", true)
|
400
|
+
return process_instruction(start_position)
|
389
401
|
else
|
390
402
|
# Get the next tag
|
391
|
-
md = @source.match(
|
403
|
+
md = @source.match(TAG_PATTERN, true)
|
392
404
|
unless md
|
405
|
+
@source.position = start_position
|
393
406
|
raise REXML::ParseException.new("malformed XML: missing tag start", @source)
|
394
407
|
end
|
408
|
+
tag = md[1]
|
395
409
|
@document_status = :in_element
|
396
410
|
prefixes = Set.new
|
397
411
|
prefixes << md[2] if md[2]
|
@@ -405,23 +419,17 @@ module REXML
|
|
405
419
|
end
|
406
420
|
|
407
421
|
if closed
|
408
|
-
@closed =
|
422
|
+
@closed = tag
|
409
423
|
@nsstack.shift
|
410
424
|
else
|
411
|
-
@tags.push(
|
425
|
+
@tags.push( tag )
|
412
426
|
end
|
413
|
-
return [ :start_element,
|
427
|
+
return [ :start_element, tag, attributes ]
|
414
428
|
end
|
415
429
|
else
|
416
|
-
md = @source.match(
|
417
|
-
|
418
|
-
|
419
|
-
end
|
420
|
-
#STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0
|
421
|
-
#return [ :text, "" ] if md[0].length == 0
|
422
|
-
# unnormalized = Text::unnormalize( md[1], self )
|
423
|
-
# return PullEvent.new( :text, md[1], unnormalized )
|
424
|
-
return [ :text, md[1] ]
|
430
|
+
md = @source.match(/([^<]*)/um, true)
|
431
|
+
text = md[1]
|
432
|
+
return [ :text, text ]
|
425
433
|
end
|
426
434
|
rescue REXML::UndefinedNamespaceException
|
427
435
|
raise
|
@@ -463,8 +471,7 @@ module REXML
|
|
463
471
|
|
464
472
|
# Unescapes all possible entities
|
465
473
|
def unnormalize( string, entities=nil, filter=nil )
|
466
|
-
rv = string.
|
467
|
-
rv.gsub!( /\r\n?/, "\n" )
|
474
|
+
rv = string.gsub( /\r\n?/, "\n" )
|
468
475
|
matches = rv.scan( REFERENCE_RE )
|
469
476
|
return rv if matches.size == 0
|
470
477
|
rv.gsub!( /�*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {
|
@@ -499,9 +506,9 @@ module REXML
|
|
499
506
|
end
|
500
507
|
|
501
508
|
def parse_name(base_error_message)
|
502
|
-
md = @source.match(
|
509
|
+
md = @source.match(NAME_PATTERN, true)
|
503
510
|
unless md
|
504
|
-
if @source.match(/\
|
511
|
+
if @source.match(/\s*\S/um)
|
505
512
|
message = "#{base_error_message}: invalid name"
|
506
513
|
else
|
507
514
|
message = "#{base_error_message}: name is missing"
|
@@ -577,97 +584,89 @@ module REXML
|
|
577
584
|
end
|
578
585
|
end
|
579
586
|
|
580
|
-
def process_instruction
|
581
|
-
match_data = @source.match(
|
587
|
+
def process_instruction(start_position)
|
588
|
+
match_data = @source.match(INSTRUCTION_END, true)
|
582
589
|
unless match_data
|
583
590
|
message = "Invalid processing instruction node"
|
591
|
+
@source.position = start_position
|
584
592
|
raise REXML::ParseException.new(message, @source)
|
585
593
|
end
|
594
|
+
if @document_status.nil? and match_data[1] == "xml"
|
595
|
+
content = match_data[2]
|
596
|
+
version = VERSION.match(content)
|
597
|
+
version = version[1] unless version.nil?
|
598
|
+
encoding = ENCODING.match(content)
|
599
|
+
encoding = encoding[1] unless encoding.nil?
|
600
|
+
if need_source_encoding_update?(encoding)
|
601
|
+
@source.encoding = encoding
|
602
|
+
end
|
603
|
+
if encoding.nil? and /\AUTF-16(?:BE|LE)\z/i =~ @source.encoding
|
604
|
+
encoding = "UTF-16"
|
605
|
+
end
|
606
|
+
standalone = STANDALONE.match(content)
|
607
|
+
standalone = standalone[1] unless standalone.nil?
|
608
|
+
return [ :xmldecl, version, encoding, standalone ]
|
609
|
+
end
|
586
610
|
[:processing_instruction, match_data[1], match_data[2]]
|
587
611
|
end
|
588
612
|
|
589
613
|
def parse_attributes(prefixes, curr_ns)
|
590
614
|
attributes = {}
|
591
615
|
closed = false
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
until scanner.eos?
|
605
|
-
if scanner.scan(/\s+/)
|
606
|
-
break if scanner.eos?
|
607
|
-
end
|
608
|
-
|
609
|
-
pos = scanner.pos
|
610
|
-
loop do
|
611
|
-
break if scanner.scan(ATTRIBUTE_PATTERN)
|
612
|
-
unless scanner.scan(QNAME)
|
613
|
-
message = "Invalid attribute name: <#{scanner.rest}>"
|
614
|
-
raise REXML::ParseException.new(message, @source)
|
615
|
-
end
|
616
|
-
name = scanner[0]
|
617
|
-
unless scanner.scan(/\s*=\s*/um)
|
616
|
+
while true
|
617
|
+
if @source.match(">", true)
|
618
|
+
return attributes, closed
|
619
|
+
elsif @source.match("/>", true)
|
620
|
+
closed = true
|
621
|
+
return attributes, closed
|
622
|
+
elsif match = @source.match(QNAME, true)
|
623
|
+
name = match[1]
|
624
|
+
prefix = match[2]
|
625
|
+
local_part = match[3]
|
626
|
+
|
627
|
+
unless @source.match(/\s*=\s*/um, true)
|
618
628
|
message = "Missing attribute equal: <#{name}>"
|
619
629
|
raise REXML::ParseException.new(message, @source)
|
620
630
|
end
|
621
|
-
|
622
|
-
unless quote
|
631
|
+
unless match = @source.match(/(['"])/, true)
|
623
632
|
message = "Missing attribute value start quote: <#{name}>"
|
624
633
|
raise REXML::ParseException.new(message, @source)
|
625
634
|
end
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
scanner << ">"
|
631
|
-
scanner << match_data[1]
|
632
|
-
scanner.pos = pos
|
633
|
-
closed = !match_data[2].nil?
|
634
|
-
next
|
635
|
-
end
|
636
|
-
message =
|
637
|
-
"Missing attribute value end quote: <#{name}>: <#{quote}>"
|
635
|
+
quote = match[1]
|
636
|
+
value = @source.read_until(quote)
|
637
|
+
unless value.chomp!(quote)
|
638
|
+
message = "Missing attribute value end quote: <#{name}>: <#{quote}>"
|
638
639
|
raise REXML::ParseException.new(message, @source)
|
639
640
|
end
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
msg = "The '
|
641
|
+
@source.match(/\s*/um, true)
|
642
|
+
if prefix == "xmlns"
|
643
|
+
if local_part == "xml"
|
644
|
+
if value != "http://www.w3.org/XML/1998/namespace"
|
645
|
+
msg = "The 'xml' prefix must not be bound to any other namespace "+
|
646
|
+
"(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
|
647
|
+
raise REXML::ParseException.new( msg, @source, self )
|
648
|
+
end
|
649
|
+
elsif local_part == "xmlns"
|
650
|
+
msg = "The 'xmlns' prefix must not be declared "+
|
650
651
|
"(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
|
651
|
-
raise REXML::ParseException.new( msg, @source, self
|
652
|
+
raise REXML::ParseException.new( msg, @source, self)
|
652
653
|
end
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
raise REXML::ParseException.new( msg, @source, self)
|
654
|
+
curr_ns << local_part
|
655
|
+
elsif prefix
|
656
|
+
prefixes << prefix unless prefix == "xml"
|
657
657
|
end
|
658
|
-
curr_ns << local_part
|
659
|
-
elsif prefix
|
660
|
-
prefixes << prefix unless prefix == "xml"
|
661
|
-
end
|
662
658
|
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
659
|
+
if attributes[name]
|
660
|
+
msg = "Duplicate attribute #{name.inspect}"
|
661
|
+
raise REXML::ParseException.new(msg, @source, self)
|
662
|
+
end
|
667
663
|
|
668
|
-
|
664
|
+
attributes[name] = value
|
665
|
+
else
|
666
|
+
message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
|
667
|
+
raise REXML::ParseException.new(message, @source)
|
668
|
+
end
|
669
669
|
end
|
670
|
-
return attributes, closed
|
671
670
|
end
|
672
671
|
end
|
673
672
|
end
|