summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 02:49:10 +0000
committerkou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-12-20 02:49:10 +0000
commit0d1abb904e9eda11dfed19181553725764a3d950 (patch)
tree0fc7858ebbc6c76f81249c37ce3ed0c31a8d5253
parent0b38221d4ea75d8ac96b2adb1f7fafb0b20f9d29 (diff)
rexml: upgrade to 3.1.8
See https://github.com/ruby/rexml/blob/master/NEWS.md for change summary. Changes for spec/ has been reported: https://github.com/ruby/spec/pull/639 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66458 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--NEWS3
-rw-r--r--lib/rexml/doctype.rb29
-rw-r--r--lib/rexml/formatters/default.rb12
-rw-r--r--lib/rexml/functions.rb2
-rw-r--r--lib/rexml/instruction.rb28
-rw-r--r--lib/rexml/namespace.rb23
-rw-r--r--lib/rexml/parsers/baseparser.rb169
-rw-r--r--lib/rexml/rexml.gemspec76
-rw-r--r--lib/rexml/rexml.rb6
-rw-r--r--lib/rexml/source.rb1
-rw-r--r--lib/rexml/text.rb44
-rw-r--r--lib/rexml/xmldecl.rb35
-rw-r--r--spec/ruby/library/rexml/attribute/inspect_spec.rb4
-rw-r--r--test/rexml/data/t75.xml2
-rw-r--r--test/rexml/formatter/test_default.rb19
-rw-r--r--test/rexml/parse/test_element.rb38
-rw-r--r--test/rexml/parse/test_processing_instruction.rb25
-rw-r--r--test/rexml/parser/test_tree.rb2
-rw-r--r--test/rexml/parser/test_ultra_light.rb2
-rw-r--r--test/rexml/test_attribute.rb14
-rw-r--r--test/rexml/test_core.rb13
-rw-r--r--test/rexml/test_doctype.rb134
-rw-r--r--test/rexml/test_instruction.rb14
-rw-r--r--test/rexml/test_stream.rb4
-rw-r--r--test/rexml/test_text.rb52
-rw-r--r--test/rexml/test_xml_declaration.rb12
26 files changed, 566 insertions, 197 deletions
diff --git a/NEWS b/NEWS
index 1bc6e6d8e9..f6480f75a8 100644
--- a/NEWS
+++ b/NEWS
@@ -453,6 +453,9 @@ sufficient information, see the ChangeLog file or Redmine
[REXML]
+ * Upgrade to REXML 3.1.8
+ https://github.com/ruby/rexml/blob/master/NEWS.md
+
[Improved some XPath implementations]
* <code>concat()</code> function: Stringify all arguments before concatenating
diff --git a/lib/rexml/doctype.rb b/lib/rexml/doctype.rb
index ca44454dec..757b639639 100644
--- a/lib/rexml/doctype.rb
+++ b/lib/rexml/doctype.rb
@@ -108,13 +108,19 @@ module REXML
# Ignored
def write( output, indent=0, transitive=false, ie_hack=false )
f = REXML::Formatters::Default.new
+ c = context
+ if c and c[:prologue_quote] == :apostrophe
+ quote = "'"
+ else
+ quote = "\""
+ end
indent( output, indent )
output << START
output << ' '
output << @name
- output << " #@external_id" if @external_id
- output << " #{@long_name.inspect}" if @long_name
- output << " #{@uri.inspect}" if @uri
+ output << " #{@external_id}" if @external_id
+ output << " #{quote}#{@long_name}#{quote}" if @long_name
+ output << " #{quote}#{@uri}#{quote}" if @uri
unless @children.empty?
output << ' ['
@children.each { |child|
@@ -127,7 +133,11 @@ module REXML
end
def context
- @parent.context
+ if @parent
+ @parent.context
+ else
+ nil
+ end
end
def entity( name )
@@ -249,9 +259,16 @@ module REXML
end
def to_s
+ c = nil
+ c = parent.context if parent
+ if c and c[:prologue_quote] == :apostrophe
+ quote = "'"
+ else
+ quote = "\""
+ end
notation = "<!NOTATION #{@name} #{@middle}"
- notation << " #{@public.inspect}" if @public
- notation << " #{@system.inspect}" if @system
+ notation << " #{quote}#{@public}#{quote}" if @public
+ notation << " #{quote}#{@system}#{quote}" if @system
notation << ">"
notation
end
diff --git a/lib/rexml/formatters/default.rb b/lib/rexml/formatters/default.rb
index c375f1468b..811b2ff3d5 100644
--- a/lib/rexml/formatters/default.rb
+++ b/lib/rexml/formatters/default.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: false
+
module REXML
module Formatters
class Default
@@ -101,11 +102,14 @@ module REXML
end
def write_instruction( node, output )
- output << Instruction::START.sub(/\\/u, '')
+ output << Instruction::START
output << node.target
- output << ' '
- output << node.content
- output << Instruction::STOP.sub(/\\/u, '')
+ content = node.content
+ if content
+ output << ' '
+ output << content
+ end
+ output << Instruction::STOP
end
end
end
diff --git a/lib/rexml/functions.rb b/lib/rexml/functions.rb
index 452426703c..219f9c8db5 100644
--- a/lib/rexml/functions.rb
+++ b/lib/rexml/functions.rb
@@ -423,7 +423,7 @@ module REXML
number = number(number)
begin
neg = number.negative?
- number = number.abs.round(half: :up)
+ number = number.abs.round
neg ? -number : number
rescue FloatDomainError
number
diff --git a/lib/rexml/instruction.rb b/lib/rexml/instruction.rb
index 2552f3e442..318741f03b 100644
--- a/lib/rexml/instruction.rb
+++ b/lib/rexml/instruction.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: false
+
require_relative "child"
require_relative "source"
@@ -6,8 +7,8 @@ module REXML
# Represents an XML Instruction; IE, <? ... ?>
# TODO: Add parent arg (3rd arg) to constructor
class Instruction < Child
- START = '<\?'
- STOP = '\?>'
+ START = "<?"
+ STOP = "?>"
# target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
# content is everything else.
@@ -17,20 +18,25 @@ module REXML
# @param target can be one of a number of things. If String, then
# the target of this instruction is set to this. If an Instruction,
# then the Instruction is shallowly cloned (target and content are
- # copied). If a Source, then the source is scanned and parsed for
- # an Instruction declaration.
+ # copied).
# @param content Must be either a String, or a Parent. Can only
# be a Parent if the target argument is a Source. Otherwise, this
# String is set as the content of this instruction.
def initialize(target, content=nil)
- if target.kind_of? String
+ case target
+ when String
super()
@target = target
@content = content
- elsif target.kind_of? Instruction
+ when Instruction
super(content)
@target = target.target
@content = target.content
+ else
+ message =
+ "processing instruction target must be String or REXML::Instruction: "
+ message << "<#{target.inspect}>"
+ raise ArgumentError, message
end
@content.strip! if @content
end
@@ -45,11 +51,13 @@ module REXML
def write writer, indent=-1, transitive=false, ie_hack=false
Kernel.warn( "#{self.class.name}.write is deprecated", uplevel: 1)
indent(writer, indent)
- writer << START.sub(/\\/u, '')
+ writer << START
writer << @target
- writer << ' '
- writer << @content
- writer << STOP.sub(/\\/u, '')
+ if @content
+ writer << ' '
+ writer << @content
+ end
+ writer << STOP
end
# @return true if other is an Instruction, and the content and target
diff --git a/lib/rexml/namespace.rb b/lib/rexml/namespace.rb
index 4a7174eaeb..924edf9506 100644
--- a/lib/rexml/namespace.rb
+++ b/lib/rexml/namespace.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: false
+
require_relative 'xmltokens'
module REXML
@@ -14,14 +15,24 @@ module REXML
# Sets the name and the expanded name
def name=( name )
@expanded_name = name
- name =~ NAMESPLIT
- if $1
- @prefix = $1
+ case name
+ when NAMESPLIT
+ if $1
+ @prefix = $1
+ else
+ @prefix = ""
+ @namespace = ""
+ end
+ @name = $2
+ when ""
+ @prefix = nil
+ @namespace = nil
+ @name = nil
else
- @prefix = ""
- @namespace = ""
+ message = "name must be \#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: "
+ message += "<#{name.inspect}>"
+ raise ArgumentError, message
end
- @name = $2
end
# Compares names optionally WITH namespaces
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 4df1f57a05..de337a74d5 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -3,6 +3,7 @@ require_relative '../parseexception'
require_relative '../undefinednamespaceexception'
require_relative '../source'
require 'set'
+require "strscan"
module REXML
module Parsers
@@ -32,9 +33,9 @@ module REXML
COMBININGCHAR = '' # TODO
EXTENDER = '' # TODO
- NCNAME_STR= "[#{LETTER}_:][-[:alnum:]._:#{COMBININGCHAR}#{EXTENDER}]*"
- NAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})"
- UNAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
+ NCNAME_STR= "[#{LETTER}_][-[:alnum:]._#{COMBININGCHAR}#{EXTENDER}]*"
+ QNAME_STR= "(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})"
+ QNAME = /(#{QNAME_STR})/
NAMECHAR = '[\-\w\.:]'
NAME = "([\\w:]#{NAMECHAR}*)"
@@ -46,7 +47,7 @@ module REXML
DOCTYPE_START = /\A\s*<!DOCTYPE\s/um
DOCTYPE_END = /\A\s*\]\s*>/um
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
- ATTRIBUTE_PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\4/um
+ ATTRIBUTE_PATTERN = /\s*(#{QNAME_STR})\s*=\s*(["'])(.*?)\4/um
COMMENT_START = /\A<!--/u
COMMENT_PATTERN = /<!--(.*?)-->/um
CDATA_START = /\A<!\[CDATA\[/u
@@ -55,9 +56,9 @@ module REXML
XMLDECL_START = /\A<\?xml\s/u;
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um
INSTRUCTION_START = /\A<\?/u
- INSTRUCTION_PATTERN = /<\?(.*?)(\s+.*?)?\?>/um
- TAG_MATCH = /^<((?>#{NAME_STR}))\s*((?>\s+#{UNAME_STR}\s*=\s*(["']).*?\5)*)\s*(\/)?>/um
- CLOSE_MATCH = /^\s*<\/(#{NAME_STR})\s*>/um
+ INSTRUCTION_PATTERN = /<\?#{NAME}(\s+.*?)?\?>/um
+ TAG_MATCH = /^<((?>#{QNAME_STR}))/um
+ CLOSE_MATCH = /^\s*<\/(#{QNAME_STR})\s*>/um
VERSION = /\bversion\s*=\s*["'](.*?)['"]/um
ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um
@@ -107,13 +108,6 @@ module REXML
"apos" => [/&apos;/, "&apos;", "'", /'/]
}
-
- ######################################################################
- # These are patterns to identify common markup errors, to make the
- # error messages more informative.
- ######################################################################
- MISSING_ATTRIBUTE_QUOTES = /^<#{NAME_STR}\s+#{NAME_STR}\s*=\s*[^"']/um
-
def initialize( source )
self.stream = source
@listeners = []
@@ -224,7 +218,7 @@ module REXML
standalone = standalone[1] unless standalone.nil?
return [ :xmldecl, version, encoding, standalone ]
when INSTRUCTION_START
- return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ]
+ return process_instruction
when DOCTYPE_START
md = @source.match( DOCTYPE_PATTERN, true )
@nsstack.unshift(curr_ns=Set.new)
@@ -336,11 +330,12 @@ module REXML
if @source.buffer[1] == ?/
@nsstack.shift
last_tag = @tags.pop
- #md = @source.match_to_consume( '>', CLOSE_MATCH)
md = @source.match( CLOSE_MATCH, true )
- raise REXML::ParseException.new( "Missing end tag for "+
- "'#{last_tag}' (got \"#{md[1]}\")",
- @source) unless last_tag == md[1]
+ if md.nil? or last_tag != md[1]
+ message = "Missing end tag for '#{last_tag}'"
+ message << " (got '#{md[1]}')" if md
+ raise REXML::ParseException.new(message, @source)
+ end
return [ :end_element, last_tag ]
elsif @source.buffer[1] == ?!
md = @source.match(/\A(\s*[^>]*>)/um)
@@ -362,52 +357,17 @@ module REXML
raise REXML::ParseException.new( "Declarations can only occur "+
"in the doctype declaration.", @source)
elsif @source.buffer[1] == ??
- md = @source.match( INSTRUCTION_PATTERN, true )
- return [ :processing_instruction, md[1], md[2] ] if md
- raise REXML::ParseException.new( "Bad instruction declaration",
- @source)
+ return process_instruction
else
# Get the next tag
md = @source.match(TAG_MATCH, true)
unless md
- # Check for missing attribute quotes
- raise REXML::ParseException.new("missing attribute quote", @source) if @source.match(MISSING_ATTRIBUTE_QUOTES )
raise REXML::ParseException.new("malformed XML: missing tag start", @source)
end
- attributes = {}
prefixes = Set.new
prefixes << md[2] if md[2]
@nsstack.unshift(curr_ns=Set.new)
- if md[4].size > 0
- attrs = md[4].scan( ATTRIBUTE_PATTERN )
- raise REXML::ParseException.new( "error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"", @source) if $' and $'.strip.size > 0
- attrs.each do |attr_name, prefix, local_part, quote, value|
- if prefix == "xmlns"
- if local_part == "xml"
- if value != "http://www.w3.org/XML/1998/namespace"
- msg = "The 'xml' prefix must not be bound to any other namespace "+
- "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
- raise REXML::ParseException.new( msg, @source, self )
- end
- elsif local_part == "xmlns"
- msg = "The 'xmlns' prefix must not be declared "+
- "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
- raise REXML::ParseException.new( msg, @source, self)
- end
- curr_ns << local_part
- elsif prefix
- prefixes << prefix unless prefix == "xml"
- end
-
- if attributes.has_key?(attr_name)
- msg = "Duplicate attribute #{attr_name.inspect}"
- raise REXML::ParseException.new(msg, @source, self)
- end
-
- attributes[attr_name] = value
- end
- end
-
+ attributes, closed = parse_attributes(prefixes, curr_ns)
# Verify that all of the prefixes have been defined
for prefix in prefixes
unless @nsstack.find{|k| k.member?(prefix)}
@@ -415,7 +375,7 @@ module REXML
end
end
- if md[6]
+ if closed
@closed = md[1]
@nsstack.shift
else
@@ -438,7 +398,7 @@ module REXML
raise
rescue REXML::ParseException
raise
- rescue Exception, NameError => error
+ rescue => error
raise REXML::ParseException.new( "Exception parsing",
@source, self, (error ? error : $!) )
end
@@ -508,6 +468,99 @@ module REXML
return false if /\AUTF-16\z/i =~ xml_declaration_encoding
true
end
+
+ def process_instruction
+ match_data = @source.match(INSTRUCTION_PATTERN, true)
+ unless match_data
+ message = "Invalid processing instruction node"
+ raise REXML::ParseException.new(message, @source)
+ end
+ [:processing_instruction, match_data[1], match_data[2]]
+ end
+
+ def parse_attributes(prefixes, curr_ns)
+ attributes = {}
+ closed = false
+ match_data = @source.match(/^(.*?)(\/)?>/um, true)
+ if match_data.nil?
+ message = "Start tag isn't ended"
+ raise REXML::ParseException.new(message, @source)
+ end
+
+ raw_attributes = match_data[1]
+ closed = !match_data[2].nil?
+ return attributes, closed if raw_attributes.nil?
+ return attributes, closed if raw_attributes.empty?
+
+ scanner = StringScanner.new(raw_attributes)
+ until scanner.eos?
+ if scanner.scan(/\s+/)
+ break if scanner.eos?
+ end
+
+ pos = scanner.pos
+ loop do
+ break if scanner.scan(ATTRIBUTE_PATTERN)
+ unless scanner.scan(QNAME)
+ message = "Invalid attribute name: <#{scanner.rest}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ name = scanner[0]
+ unless scanner.scan(/\s*=\s*/um)
+ message = "Missing attribute equal: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ quote = scanner.scan(/['"]/)
+ unless quote
+ message = "Missing attribute value start quote: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
+ match_data = @source.match(/^(.*?)(\/)?>/um, true)
+ if match_data
+ scanner << "/" if closed
+ scanner << ">"
+ scanner << match_data[1]
+ scanner.pos = pos
+ closed = !match_data[2].nil?
+ next
+ end
+ message =
+ "Missing attribute value end quote: <#{name}>: <#{quote}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+ name = scanner[1]
+ prefix = scanner[2]
+ local_part = scanner[3]
+ # quote = scanner[4]
+ value = scanner[5]
+ if prefix == "xmlns"
+ if local_part == "xml"
+ if value != "http://www.w3.org/XML/1998/namespace"
+ msg = "The 'xml' prefix must not be bound to any other namespace "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self )
+ end
+ elsif local_part == "xmlns"
+ msg = "The 'xmlns' prefix must not be declared "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self)
+ end
+ curr_ns << local_part
+ elsif prefix
+ prefixes << prefix unless prefix == "xml"
+ end
+
+ if attributes.has_key?(name)
+ msg = "Duplicate attribute #{name.inspect}"
+ raise REXML::ParseException.new(msg, @source, self)
+ end
+
+ attributes[name] = value
+ end
+ return attributes, closed
+ end
end
end
end
diff --git a/lib/rexml/rexml.gemspec b/lib/rexml/rexml.gemspec
index f9acda0698..8362ca3c3a 100644
--- a/lib/rexml/rexml.gemspec
+++ b/lib/rexml/rexml.gemspec
@@ -16,23 +16,65 @@ Gem::Specification.new do |spec|
spec.homepage = "https://github.com/ruby/rexml"
spec.license = "BSD-2-Clause"
- spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile",
- "bin/console", "bin/setup", "lib/rexml/attlistdecl.rb", "lib/rexml/attribute.rb", "lib/rexml/cdata.rb",
- "lib/rexml/child.rb", "lib/rexml/comment.rb", "lib/rexml/doctype.rb", "lib/rexml/document.rb",
- "lib/rexml/dtd/attlistdecl.rb", "lib/rexml/dtd/dtd.rb", "lib/rexml/dtd/elementdecl.rb",
- "lib/rexml/dtd/entitydecl.rb", "lib/rexml/dtd/notationdecl.rb", "lib/rexml/element.rb",
- "lib/rexml/encoding.rb", "lib/rexml/entity.rb", "lib/rexml/formatters/default.rb",
- "lib/rexml/formatters/pretty.rb", "lib/rexml/formatters/transitive.rb", "lib/rexml/functions.rb",
- "lib/rexml/instruction.rb", "lib/rexml/light/node.rb", "lib/rexml/namespace.rb", "lib/rexml/node.rb",
- "lib/rexml/output.rb", "lib/rexml/parent.rb", "lib/rexml/parseexception.rb", "lib/rexml/parsers/baseparser.rb",
- "lib/rexml/parsers/lightparser.rb", "lib/rexml/parsers/pullparser.rb", "lib/rexml/parsers/sax2parser.rb",
- "lib/rexml/parsers/streamparser.rb", "lib/rexml/parsers/treeparser.rb",
- "lib/rexml/parsers/ultralightparser.rb", "lib/rexml/parsers/xpathparser.rb", "lib/rexml/quickpath.rb",
- "lib/rexml/rexml.rb", "lib/rexml/sax2listener.rb", "lib/rexml/security.rb", "lib/rexml/source.rb",
- "lib/rexml/streamlistener.rb", "lib/rexml/syncenumerator.rb", "lib/rexml/text.rb",
- "lib/rexml/undefinednamespaceexception.rb", "lib/rexml/validation/relaxng.rb",
- "lib/rexml/validation/validation.rb", "lib/rexml/validation/validationexception.rb", "lib/rexml/xmldecl.rb",
- "lib/rexml/xmltokens.rb", "lib/rexml/xpath.rb", "lib/rexml/xpath_parser.rb", "rexml.gemspec"]
+ spec.files = [
+ ".gitignore",
+ ".travis.yml",
+ "Gemfile",
+ "LICENSE.txt",
+ "README.md",
+ "Rakefile",
+ "lib/rexml/attlistdecl.rb",
+ "lib/rexml/attribute.rb",
+ "lib/rexml/cdata.rb",
+ "lib/rexml/child.rb",
+ "lib/rexml/comment.rb",
+ "lib/rexml/doctype.rb",
+ "lib/rexml/document.rb",
+ "lib/rexml/dtd/attlistdecl.rb",
+ "lib/rexml/dtd/dtd.rb",
+ "lib/rexml/dtd/elementdecl.rb",
+ "lib/rexml/dtd/entitydecl.rb",
+ "lib/rexml/dtd/notationdecl.rb",
+ "lib/rexml/element.rb",
+ "lib/rexml/encoding.rb",
+ "lib/rexml/entity.rb",
+ "lib/rexml/formatters/default.rb",
+ "lib/rexml/formatters/pretty.rb",
+ "lib/rexml/formatters/transitive.rb",
+ "lib/rexml/functions.rb",
+ "lib/rexml/instruction.rb",
+ "lib/rexml/light/node.rb",
+ "lib/rexml/namespace.rb",
+ "lib/rexml/node.rb",
+ "lib/rexml/output.rb",
+ "lib/rexml/parent.rb",
+ "lib/rexml/parseexception.rb",
+ "lib/rexml/parsers/baseparser.rb",
+ "lib/rexml/parsers/lightparser.rb",
+ "lib/rexml/parsers/pullparser.rb",
+ "lib/rexml/parsers/sax2parser.rb",
+ "lib/rexml/parsers/streamparser.rb",
+ "lib/rexml/parsers/treeparser.rb",
+ "lib/rexml/parsers/ultralightparser.rb",
+ "lib/rexml/parsers/xpathparser.rb",
+ "lib/rexml/quickpath.rb",
+ "lib/rexml/rexml.rb",
+ "lib/rexml/sax2listener.rb",
+ "lib/rexml/security.rb",
+ "lib/rexml/source.rb",
+ "lib/rexml/streamlistener.rb",
+ "lib/rexml/syncenumerator.rb",
+ "lib/rexml/text.rb",
+ "lib/rexml/undefinednamespaceexception.rb",
+ "lib/rexml/validation/relaxng.rb",
+ "lib/rexml/validation/validation.rb",
+ "lib/rexml/validation/validationexception.rb",
+ "lib/rexml/xmldecl.rb",
+ "lib/rexml/xmltokens.rb",
+ "lib/rexml/xpath.rb",
+ "lib/rexml/xpath_parser.rb",
+ "rexml.gemspec",
+ ]
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
diff --git a/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
index fbc0d339d8..654f54799f 100644
--- a/lib/rexml/rexml.rb
+++ b/lib/rexml/rexml.rb
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
# REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby.
#
@@ -24,8 +24,8 @@
module REXML
COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
DATE = "2008/019"
- VERSION = "3.1.7.3"
- REVISION = %w$Revision$[1] || ''
+ VERSION = "3.1.8"
+ REVISION = ""
Copyright = COPYRIGHT
Version = VERSION
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index 8663e489a8..770aefc818 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -254,6 +254,7 @@ module REXML
end
rescue
end
+ @er_source.seek(pos)
rescue IOError
pos = -1
line = -1
diff --git a/lib/rexml/text.rb b/lib/rexml/text.rb
index 208febf5ee..6139caecd7 100644
--- a/lib/rexml/text.rb
+++ b/lib/rexml/text.rb
@@ -96,27 +96,28 @@ module REXML
@raw = false
@parent = nil
+ @entity_filter = nil
if parent
super( parent )
@raw = parent.raw
end
- @raw = raw unless raw.nil?
- @entity_filter = entity_filter
- clear_cache
-
if arg.kind_of? String
@string = arg.dup
- @string.squeeze!(" \n\t") unless respect_whitespace
elsif arg.kind_of? Text
- @string = arg.to_s
+ @string = arg.instance_variable_get(:@string).dup
@raw = arg.raw
+ @entity_filter = arg.instance_variable_get(:@entity_filter)
elsif
raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
end
- @string.gsub!( /\r\n?/, "\n" )
+ @string.squeeze!(" \n\t") unless respect_whitespace
+ @string.gsub!(/\r\n?/, "\n")
+ @raw = raw unless raw.nil?
+ @entity_filter = entity_filter if entity_filter
+ clear_cache
Text.check(@string, illegal, doctype) if @raw
end
@@ -181,7 +182,7 @@ module REXML
def clone
- return Text.new(self)
+ return Text.new(self, true)
end
@@ -226,9 +227,7 @@ module REXML
# u.to_s #-> "sean russell"
def to_s
return @string if @raw
- return @normalized if @normalized
-
- @normalized = Text::normalize( @string, doctype, @entity_filter )
+ @normalized ||= Text::normalize( @string, doctype, @entity_filter )
end
def inspect
@@ -249,8 +248,7 @@ module REXML
# u = Text.new( "sean russell", false, nil, true )
# u.value #-> "sean russell"
def value
- return @unnormalized if @unnormalized
- @unnormalized = Text::unnormalize( @string, doctype )
+ @unnormalized ||= Text::unnormalize( @string, doctype )
end
# Sets the contents of this text node. This expects the text to be
@@ -266,16 +264,16 @@ module REXML
@raw = false
end
- def wrap(string, width, addnewline=false)
- # Recursively wrap string at width.
- return string if string.length <= width
- place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
- if addnewline then
- return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
- else
- return string[0,place] + "\n" + wrap(string[place+1..-1], width)
- end
- end
+ def wrap(string, width, addnewline=false)
+ # Recursively wrap string at width.
+ return string if string.length <= width
+ place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
+ if addnewline then
+ return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ else
+ return string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ end
+ end
def indent_text(string, level=1, style="\t", indentfirstline=true)
return string if level < 0
diff --git a/lib/rexml/xmldecl.rb b/lib/rexml/xmldecl.rb
index d02204931c..89c0747d49 100644
--- a/lib/rexml/xmldecl.rb
+++ b/lib/rexml/xmldecl.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: false
+
require_relative 'encoding'
require_relative 'source'
@@ -7,11 +8,11 @@ module REXML
class XMLDecl < Child
include Encoding
- DEFAULT_VERSION = "1.0";
- DEFAULT_ENCODING = "UTF-8";
- DEFAULT_STANDALONE = "no";
- START = '<\?xml';
- STOP = '\?>';
+ DEFAULT_VERSION = "1.0"
+ DEFAULT_ENCODING = "UTF-8"
+ DEFAULT_STANDALONE = "no"
+ START = "<?xml"
+ STOP = "?>"
attr_accessor :version, :standalone
attr_reader :writeencoding, :writethis
@@ -46,9 +47,9 @@ module REXML
# Ignored
def write(writer, indent=-1, transitive=false, ie_hack=false)
return nil unless @writethis or writer.kind_of? Output
- writer << START.sub(/\\/u, '')
+ writer << START
writer << " #{content encoding}"
- writer << STOP.sub(/\\/u, '')
+ writer << STOP
end
def ==( other )
@@ -102,14 +103,26 @@ module REXML
end
def inspect
- START.sub(/\\/u, '') + " ... " + STOP.sub(/\\/u, '')
+ "#{START} ... #{STOP}"
end
private
def content(enc)
- rv = "version='#@version'"
- rv << " encoding='#{enc}'" if @writeencoding || enc !~ /\Autf-8\z/i
- rv << " standalone='#@standalone'" if @standalone
+ context = nil
+ context = parent.context if parent
+ if context and context[:prologue_quote] == :quote
+ quote = "\""
+ else
+ quote = "'"
+ end
+
+ rv = "version=#{quote}#{@version}#{quote}"
+ if @writeencoding or enc !~ /\Autf-8\z/i
+ rv << " encoding=#{quote}#{enc}#{quote}"
+ end
+ if @standalone
+ rv << " standalone=#{quote}#{@standalone}#{quote}"
+ end
rv
end
end
diff --git a/spec/ruby/library/rexml/attribute/inspect_spec.rb b/spec/ruby/library/rexml/attribute/inspect_spec.rb
index 86a437ec74..632b477cca 100644
--- a/spec/ruby/library/rexml/attribute/inspect_spec.rb
+++ b/spec/ruby/library/rexml/attribute/inspect_spec.rb
@@ -13,7 +13,7 @@ describe "REXML::Attribute#inspect" do
end
it "does not escape text" do
- a = REXML::Attribute.new("&&", "<>")
- a.inspect.should == "&&='<>'"
+ a = REXML::Attribute.new("name", "<>")
+ a.inspect.should == "name='<>'"
end
end
diff --git a/test/rexml/data/t75.xml b/test/rexml/data/t75.xml
index 0911fb1b1a..eb3cccee4b 100644
--- a/test/rexml/data/t75.xml
+++ b/test/rexml/data/t75.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="ISO-8859-1"?><?pos="3"?>
+<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generated by hnb 1.9.17 (http://hnb.sourceforge.net) -->
<!DOCTYPE tree[
diff --git a/test/rexml/formatter/test_default.rb b/test/rexml/formatter/test_default.rb
new file mode 100644
index 0000000000..b5b131724b
--- /dev/null
+++ b/test/rexml/formatter/test_default.rb
@@ -0,0 +1,19 @@
+require_relative "../rexml_test_utils"
+
+module REXMLTests
+ class DefaultFormatterTest < Test::Unit::TestCase
+ def format(node)
+ formatter = REXML::Formatters::Default.new
+ output = ""
+ formatter.write(node, output)
+ output
+ end
+
+ class InstructionTest < self
+ def test_content_nil
+ instruction = REXML::Instruction.new("target")
+ assert_equal("<?target?>", format(instruction))
+ end
+ end
+ end
+end
diff --git a/test/rexml/parse/test_element.rb b/test/rexml/parse/test_element.rb
new file mode 100644
index 0000000000..aad915fe7b
--- /dev/null
+++ b/test/rexml/parse/test_element.rb
@@ -0,0 +1,38 @@
+require "test/unit"
+require "rexml/document"
+
+module REXMLTests
+ class TestParseElement < Test::Unit::TestCase
+ def parse(xml)
+ REXML::Document.new(xml)
+ end
+
+ class TestInvalid < self
+ def test_no_end_tag
+ exception = assert_raise(REXML::ParseException) do
+ parse("<a></")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Missing end tag for 'a'
+Line: 1
+Position: 5
+Last 80 unconsumed characters:
+</
+ DETAIL
+ end
+
+ def test_empty_namespace_attribute_name
+ exception = assert_raise(REXML::ParseException) do
+ parse("<x :a=\"\"></x>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Invalid attribute name: <:a="">
+Line: 1
+Position: 9
+Last 80 unconsumed characters:
+
+ DETAIL
+ end
+ end
+ end
+end
diff --git a/test/rexml/parse/test_processing_instruction.rb b/test/rexml/parse/test_processing_instruction.rb
new file mode 100644
index 0000000000..a23513fc6e
--- /dev/null
+++ b/test/rexml/parse/test_processing_instruction.rb
@@ -0,0 +1,25 @@
+require "test/unit"
+require "rexml/document"
+
+module REXMLTests
+ class TestParseProcessinInstruction < Test::Unit::TestCase
+ def parse(xml)
+ REXML::Document.new(xml)
+ end
+
+ class TestInvalid < self
+ def test_no_name
+ exception = assert_raise(REXML::ParseException) do
+ parse("<??>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Invalid processing instruction node
+Line: 1
+Position: 4
+Last 80 unconsumed characters:
+<??>
+ DETAIL
+ end
+ end
+ end
+end
diff --git a/test/rexml/parser/test_tree.rb b/test/rexml/parser/test_tree.rb
index 7ab0addca1..8a5d9d1223 100644
--- a/test/rexml/parser/test_tree.rb
+++ b/test/rexml/parser/test_tree.rb
@@ -12,7 +12,7 @@ class TestTreeParser < Test::Unit::TestCase
parse(xml)
end
assert_equal(<<-MESSAGE, exception.to_s)
-Missing end tag for 'root' (got "not-root")
+Missing end tag for 'root' (got 'not-root')
Line: 1
Position: #{xml.bytesize}
Last 80 unconsumed characters:
diff --git a/test/rexml/parser/test_ultra_light.rb b/test/rexml/parser/test_ultra_light.rb
index c48a13d311..8f4a3980d5 100644
--- a/test/rexml/parser/test_ultra_light.rb
+++ b/test/rexml/parser/test_ultra_light.rb
@@ -55,7 +55,7 @@ class TestUltraLightParser < Test::Unit::TestCase
normalized_doctype[1] = normalized_parent
normalized_doctype
when :start_element
- tag, parent, name, attributes, *children = child
+ tag, _parent, name, attributes, *children = child
normalized_parent = :parent
normalized_children = children.collect do |sub_child|
normalize_child(sub_child)
diff --git a/test/rexml/test_attribute.rb b/test/rexml/test_attribute.rb
new file mode 100644
index 0000000000..5175bd4454
--- /dev/null
+++ b/test/rexml/test_attribute.rb
@@ -0,0 +1,14 @@
+require_relative "rexml_test_utils"
+
+module REXMLTests
+ class AttributeTest < Test::Unit::TestCase
+ def test_empty_prefix
+ error = assert_raise(ArgumentError) do
+ REXML::Attribute.new(":x")
+ end
+ assert_equal("name must be " +
+ "\#{PREFIX}:\#{LOCAL_NAME} or \#{LOCAL_NAME}: <\":x\">",
+ error.message)
+ end
+ end
+end
diff --git a/test/rexml/test_core.rb b/test/rexml/test_core.rb
index b2e5299f39..46036d7f12 100644
--- a/test/rexml/test_core.rb
+++ b/test/rexml/test_core.rb
@@ -1274,14 +1274,15 @@ EOL
def test_ticket_21
src = "<foo bar=value/>"
- assert_raise( ParseException, "invalid XML should be caught" ) {
+ exception = assert_raise(ParseException) do
Document.new(src)
- }
- begin
- Document.new(src)
- rescue
- assert_match( /missing attribute quote/, $!.message )
end
+ assert_equal(<<-DETAIL, exception.to_s)
+Missing attribute value start quote: <bar>
+Line: 1
+Position: 16
+Last 80 unconsumed characters:
+ DETAIL
end
def test_ticket_63
diff --git a/test/rexml/test_doctype.rb b/test/rexml/test_doctype.rb
index 91de05b05f..7f42669170 100644
--- a/test/rexml/test_doctype.rb
+++ b/test/rexml/test_doctype.rb
@@ -1,68 +1,92 @@
# frozen_string_literal: false
-require 'test/unit'
-require 'rexml/document'
+
+require_relative "rexml_test_utils"
module REXMLTests
class TestDocTypeAccessor < Test::Unit::TestCase
-
def setup
@sysid = "urn:x-test:sysid1"
- @notid1 = "urn:x-test:notation1"
- @notid2 = "urn:x-test:notation2"
- document_string1 = <<-"XMLEND"
- <!DOCTYPE r SYSTEM "#{@sysid}" [
- <!NOTATION n1 SYSTEM "#{@notid1}">
- <!NOTATION n2 SYSTEM "#{@notid2}">
+ @notation_id1 = "urn:x-test:notation1"
+ @notation_id2 = "urn:x-test:notation2"
+ xml_system = <<-XML
+ <!DOCTYPE root SYSTEM "#{@sysid}" [
+ <!NOTATION n1 SYSTEM "#{@notation_id1}">
+ <!NOTATION n2 SYSTEM "#{@notation_id2}">
]>
- <r/>
- XMLEND
- @doctype1 = REXML::Document.new(document_string1).doctype
+ <root/>
+ XML
+ @doc_type_system = REXML::Document.new(xml_system).doctype
@pubid = "TEST_ID"
- document_string2 = <<-"XMLEND"
- <!DOCTYPE r PUBLIC "#{@pubid}">
- <r/>
- XMLEND
- @doctype2 = REXML::Document.new(document_string2).doctype
+ xml_public = <<-XML
+ <!DOCTYPE root PUBLIC "#{@pubid}">
+ <root/>
+ XML
+ @doc_type_public = REXML::Document.new(xml_public).doctype
+
+ xml_public_system = <<-XML
+ <!DOCTYPE root PUBLIC "#{@pubid}" "#{@sysid}">
+ <root/>
+ XML
+ @doc_type_public_system = REXML::Document.new(xml_public_system).doctype
+ end
- document_string3 = <<-"XMLEND"
- <!DOCTYPE r PUBLIC "#{@pubid}" "#{@sysid}">
- <r/>
- XMLEND
- @doctype3 = REXML::Document.new(document_string3).doctype
+ def test_public
+ assert_equal([
+ nil,
+ @pubid,
+ @pubid,
+ ],
+ [
+ @doc_type_system.public,
+ @doc_type_public.public,
+ @doc_type_public_system.public,
+ ])
+ end
+ def test_to_s
+ assert_equal("<!DOCTYPE root PUBLIC \"#{@pubid}\" \"#{@sysid}\">",
+ @doc_type_public_system.to_s)
end
- def test_public
- assert_equal(nil, @doctype1.public)
- assert_equal(@pubid, @doctype2.public)
- assert_equal(@pubid, @doctype3.public)
+ def test_to_s_apostrophe
+ @doc_type_public_system.parent.context[:prologue_quote] = :apostrophe
+ assert_equal("<!DOCTYPE root PUBLIC '#{@pubid}' '#{@sysid}'>",
+ @doc_type_public_system.to_s)
end
def test_system
- assert_equal(@sysid, @doctype1.system)
- assert_equal(nil, @doctype2.system)
- assert_equal(@sysid, @doctype3.system)
+ assert_equal([
+ @sysid,
+ nil,
+ @sysid,
+ ],
+ [
+ @doc_type_system.system,
+ @doc_type_public.system,
+ @doc_type_public_system.system,
+ ])
end
def test_notation
- assert_equal(@notid1, @doctype1.notation("n1").system)
- assert_equal(@notid2, @doctype1.notation("n2").system)
+ assert_equal([
+ @notation_id1,
+ @notation_id2,
+ ],
+ [
+ @doc_type_system.notation("n1").system,
+ @doc_type_system.notation("n2").system,
+ ])
end
def test_notations
- notations = @doctype1.notations
- assert_equal(2, notations.length)
- assert_equal(@notid1, find_notation(notations, "n1").system)
- assert_equal(@notid2, find_notation(notations, "n2").system)
- end
-
- def find_notation(notations, name)
- notations.find { |notation|
- name == notation.name
- }
+ notations = @doc_type_system.notations
+ assert_equal([
+ @notation_id1,
+ @notation_id2,
+ ],
+ notations.collect(&:system))
end
-
end
class TestNotationDeclPublic < Test::Unit::TestCase
@@ -82,6 +106,19 @@ module REXMLTests
decl(@id, @uri).to_s)
end
+ def test_to_s_apostrophe
+ document = REXML::Document.new(<<-XML)
+ <!DOCTYPE root SYSTEM "urn:x-test:sysid" [
+ #{decl(@id, @uri).to_s}
+ ]>
+ <root/>
+ XML
+ document.context[:prologue_quote] = :apostrophe
+ notation = document.doctype.notations[0]
+ assert_equal("<!NOTATION #{@name} PUBLIC '#{@id}' '#{@uri}'>",
+ notation.to_s)
+ end
+
private
def decl(id, uri)
REXML::NotationDecl.new(@name, "PUBLIC", id, uri)
@@ -99,6 +136,19 @@ module REXMLTests
decl(@id).to_s)
end
+ def test_to_s_apostrophe
+ document = REXML::Document.new(<<-XML)
+ <!DOCTYPE root SYSTEM "urn:x-test:sysid" [
+ #{decl(@id).to_s}
+ ]>
+ <root/>
+ XML
+ document.context[:prologue_quote] = :apostrophe
+ notation = document.doctype.notations[0]
+ assert_equal("<!NOTATION #{@name} SYSTEM '#{@id}'>",
+ notation.to_s)
+ end
+
private
def decl(id)
REXML::NotationDecl.new(@name, "SYSTEM", id, nil)
diff --git a/test/rexml/test_instruction.rb b/test/rexml/test_instruction.rb
new file mode 100644
index 0000000000..96fa909e17
--- /dev/null
+++ b/test/rexml/test_instruction.rb
@@ -0,0 +1,14 @@
+require_relative "rexml_test_utils"
+
+module REXMLTests
+ class InstructionTest < Test::Unit::TestCase
+ def test_target_nil
+ error = assert_raise(ArgumentError) do
+ REXML::Instruction.new(nil)
+ end
+ assert_equal("processing instruction target must be String or " +
+ "REXML::Instruction: <nil>",
+ error.message)
+ end
+ end
+end
diff --git a/test/rexml/test_stream.rb b/test/rexml/test_stream.rb
index d7ceedc70e..08d4462ef9 100644
--- a/test/rexml/test_stream.rb
+++ b/test/rexml/test_stream.rb
@@ -15,8 +15,8 @@ module REXMLTests
def test_listener
data = %Q{<session1 user="han" password="rootWeiler" />\n<session2 user="han" password="rootWeiler" />}
- b = RequestReader.new( data )
- b = RequestReader.new( data )
+ RequestReader.new( data )
+ RequestReader.new( data )
end
def test_ticket_49
diff --git a/test/rexml/test_text.rb b/test/rexml/test_text.rb
index 3f8036eee3..a27701fce5 100644
--- a/test/rexml/test_text.rb
+++ b/test/rexml/test_text.rb
@@ -5,6 +5,52 @@ module REXMLTests
class TextTester < Test::Unit::TestCase
include REXML
+ def test_new_text_response_whitespace_default
+ text = Text.new("a b\t\tc", true)
+ assert_equal("a b\tc", Text.new(text).to_s)
+ end
+
+ def test_new_text_response_whitespace_true
+ text = Text.new("a b\t\tc", true)
+ assert_equal("a b\t\tc", Text.new(text, true).to_s)
+ end
+
+ def test_new_text_raw_default
+ text = Text.new("&amp;lt;", false, nil, true)
+ assert_equal("&amp;lt;", Text.new(text).to_s)
+ end
+
+ def test_new_text_raw_false
+ text = Text.new("&amp;lt;", false, nil, true)
+ assert_equal("&amp;amp;lt;", Text.new(text, false, nil, false).to_s)
+ end
+
+ def test_new_text_entity_filter_default
+ document = REXML::Document.new(<<-XML)
+<!DOCTYPE root [
+ <!ENTITY a "aaa">
+ <!ENTITY b "bbb">
+]>
+<root/>
+ XML
+ text = Text.new("aaa bbb", false, document.root, nil, ["a"])
+ assert_equal("aaa &b;",
+ Text.new(text, false, document.root).to_s)
+ end
+
+ def test_new_text_entity_filter_custom
+ document = REXML::Document.new(<<-XML)
+<!DOCTYPE root [
+ <!ENTITY a "aaa">
+ <!ENTITY b "bbb">
+]>
+<root/>
+ XML
+ text = Text.new("aaa bbb", false, document.root, nil, ["a"])
+ assert_equal("&a; bbb",
+ Text.new(text, false, document.root, nil, ["b"]).to_s)
+ end
+
def test_shift_operator_chain
text = Text.new("original\r\n")
text << "append1\r\n" << "append2\r\n"
@@ -18,5 +64,11 @@ module REXMLTests
text << "append3\r\n" << "append4\r\n"
assert_equal("original\nappend1\nappend2\nappend3\nappend4\n", text.to_s)
end
+
+ def test_clone
+ text = Text.new("&amp;lt; <")
+ assert_equal(text.to_s,
+ text.clone.to_s)
+ end
end
end
diff --git a/test/rexml/test_xml_declaration.rb b/test/rexml/test_xml_declaration.rb
index a4d97c41d0..1d5a6d312f 100644
--- a/test/rexml/test_xml_declaration.rb
+++ b/test/rexml/test_xml_declaration.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# frozen_string_literal: false
#
# Created by Henrik Mårtensson on 2007-02-18.
@@ -10,11 +9,11 @@ require "test/unit"
module REXMLTests
class TestXmlDeclaration < Test::Unit::TestCase
def setup
- xml = <<-'END_XML'
+ xml = <<-XML
<?xml encoding= 'UTF-8' standalone='yes'?>
<root>
</root>
- END_XML
+ XML
@doc = REXML::Document.new xml
@root = @doc.root
@xml_declaration = @doc.children[0]
@@ -32,5 +31,12 @@ module REXMLTests
assert_kind_of(REXML::XMLDecl, @root.previous_sibling.previous_sibling)
assert_kind_of(REXML::Element, @xml_declaration.next_sibling.next_sibling)
end
+
+ def test_write_prologue_quote
+ @doc.context[:prologue_quote] = :quote
+ assert_equal("<?xml version=\"1.0\" " +
+ "encoding=\"UTF-8\" standalone=\"yes\"?>",
+ @xml_declaration.to_s)
+ end
end
end