mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
0d1abb904e
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
287 lines
7 KiB
Ruby
287 lines
7 KiB
Ruby
# frozen_string_literal: false
|
|
require_relative "parent"
|
|
require_relative "parseexception"
|
|
require_relative "namespace"
|
|
require_relative 'entity'
|
|
require_relative 'attlistdecl'
|
|
require_relative 'xmltokens'
|
|
|
|
module REXML
|
|
# Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
|
|
# ... >. DOCTYPES can be used to declare the DTD of a document, as well as
|
|
# being used to declare entities used in the document.
|
|
class DocType < Parent
|
|
include XMLTokens
|
|
START = "<!DOCTYPE"
|
|
STOP = ">"
|
|
SYSTEM = "SYSTEM"
|
|
PUBLIC = "PUBLIC"
|
|
DEFAULT_ENTITIES = {
|
|
'gt'=>EntityConst::GT,
|
|
'lt'=>EntityConst::LT,
|
|
'quot'=>EntityConst::QUOT,
|
|
"apos"=>EntityConst::APOS
|
|
}
|
|
|
|
# name is the name of the doctype
|
|
# external_id is the referenced DTD, if given
|
|
attr_reader :name, :external_id, :entities, :namespaces
|
|
|
|
# Constructor
|
|
#
|
|
# dt = DocType.new( 'foo', '-//I/Hate/External/IDs' )
|
|
# # <!DOCTYPE foo '-//I/Hate/External/IDs'>
|
|
# dt = DocType.new( doctype_to_clone )
|
|
# # Incomplete. Shallow clone of doctype
|
|
#
|
|
# +Note+ that the constructor:
|
|
#
|
|
# Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) )
|
|
#
|
|
# is _deprecated_. Do not use it. It will probably disappear.
|
|
def initialize( first, parent=nil )
|
|
@entities = DEFAULT_ENTITIES
|
|
@long_name = @uri = nil
|
|
if first.kind_of? String
|
|
super()
|
|
@name = first
|
|
@external_id = parent
|
|
elsif first.kind_of? DocType
|
|
super( parent )
|
|
@name = first.name
|
|
@external_id = first.external_id
|
|
elsif first.kind_of? Array
|
|
super( parent )
|
|
@name = first[0]
|
|
@external_id = first[1]
|
|
@long_name = first[2]
|
|
@uri = first[3]
|
|
elsif first.kind_of? Source
|
|
super( parent )
|
|
parser = Parsers::BaseParser.new( first )
|
|
event = parser.pull
|
|
if event[0] == :start_doctype
|
|
@name, @external_id, @long_name, @uri, = event[1..-1]
|
|
end
|
|
else
|
|
super()
|
|
end
|
|
end
|
|
|
|
def node_type
|
|
:doctype
|
|
end
|
|
|
|
def attributes_of element
|
|
rv = []
|
|
each do |child|
|
|
child.each do |key,val|
|
|
rv << Attribute.new(key,val)
|
|
end if child.kind_of? AttlistDecl and child.element_name == element
|
|
end
|
|
rv
|
|
end
|
|
|
|
def attribute_of element, attribute
|
|
att_decl = find do |child|
|
|
child.kind_of? AttlistDecl and
|
|
child.element_name == element and
|
|
child.include? attribute
|
|
end
|
|
return nil unless att_decl
|
|
att_decl[attribute]
|
|
end
|
|
|
|
def clone
|
|
DocType.new self
|
|
end
|
|
|
|
# output::
|
|
# Where to write the string
|
|
# indent::
|
|
# An integer. If -1, no indentation will be used; otherwise, the
|
|
# indentation will be this number of spaces, and children will be
|
|
# indented an additional amount.
|
|
# transitive::
|
|
# Ignored
|
|
# ie_hack::
|
|
# 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 << " #{quote}#{@long_name}#{quote}" if @long_name
|
|
output << " #{quote}#{@uri}#{quote}" if @uri
|
|
unless @children.empty?
|
|
output << ' ['
|
|
@children.each { |child|
|
|
output << "\n"
|
|
f.write( child, output )
|
|
}
|
|
output << "\n]"
|
|
end
|
|
output << STOP
|
|
end
|
|
|
|
def context
|
|
if @parent
|
|
@parent.context
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def entity( name )
|
|
@entities[name].unnormalized if @entities[name]
|
|
end
|
|
|
|
def add child
|
|
super(child)
|
|
@entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES
|
|
@entities[ child.name ] = child if child.kind_of? Entity
|
|
end
|
|
|
|
# This method retrieves the public identifier identifying the document's
|
|
# DTD.
|
|
#
|
|
# Method contributed by Henrik Martensson
|
|
def public
|
|
case @external_id
|
|
when "SYSTEM"
|
|
nil
|
|
when "PUBLIC"
|
|
strip_quotes(@long_name)
|
|
end
|
|
end
|
|
|
|
# This method retrieves the system identifier identifying the document's DTD
|
|
#
|
|
# Method contributed by Henrik Martensson
|
|
def system
|
|
case @external_id
|
|
when "SYSTEM"
|
|
strip_quotes(@long_name)
|
|
when "PUBLIC"
|
|
@uri.kind_of?(String) ? strip_quotes(@uri) : nil
|
|
end
|
|
end
|
|
|
|
# This method returns a list of notations that have been declared in the
|
|
# _internal_ DTD subset. Notations in the external DTD subset are not
|
|
# listed.
|
|
#
|
|
# Method contributed by Henrik Martensson
|
|
def notations
|
|
children().select {|node| node.kind_of?(REXML::NotationDecl)}
|
|
end
|
|
|
|
# Retrieves a named notation. Only notations declared in the internal
|
|
# DTD subset can be retrieved.
|
|
#
|
|
# Method contributed by Henrik Martensson
|
|
def notation(name)
|
|
notations.find { |notation_decl|
|
|
notation_decl.name == name
|
|
}
|
|
end
|
|
|
|
private
|
|
|
|
# Method contributed by Henrik Martensson
|
|
def strip_quotes(quoted_string)
|
|
quoted_string =~ /^[\'\"].*[\'\"]$/ ?
|
|
quoted_string[1, quoted_string.length-2] :
|
|
quoted_string
|
|
end
|
|
end
|
|
|
|
# We don't really handle any of these since we're not a validating
|
|
# parser, so we can be pretty dumb about them. All we need to be able
|
|
# to do is spew them back out on a write()
|
|
|
|
# This is an abstract class. You never use this directly; it serves as a
|
|
# parent class for the specific declarations.
|
|
class Declaration < Child
|
|
def initialize src
|
|
super()
|
|
@string = src
|
|
end
|
|
|
|
def to_s
|
|
@string+'>'
|
|
end
|
|
|
|
# == DEPRECATED
|
|
# See REXML::Formatters
|
|
#
|
|
def write( output, indent )
|
|
output << to_s
|
|
end
|
|
end
|
|
|
|
public
|
|
class ElementDecl < Declaration
|
|
def initialize( src )
|
|
super
|
|
end
|
|
end
|
|
|
|
class ExternalEntity < Child
|
|
def initialize( src )
|
|
super()
|
|
@entity = src
|
|
end
|
|
def to_s
|
|
@entity
|
|
end
|
|
def write( output, indent )
|
|
output << @entity
|
|
end
|
|
end
|
|
|
|
class NotationDecl < Child
|
|
attr_accessor :public, :system
|
|
def initialize name, middle, pub, sys
|
|
super(nil)
|
|
@name = name
|
|
@middle = middle
|
|
@public = pub
|
|
@system = sys
|
|
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 << " #{quote}#{@public}#{quote}" if @public
|
|
notation << " #{quote}#{@system}#{quote}" if @system
|
|
notation << ">"
|
|
notation
|
|
end
|
|
|
|
def write( output, indent=-1 )
|
|
output << to_s
|
|
end
|
|
|
|
# This method retrieves the name of the notation.
|
|
#
|
|
# Method contributed by Henrik Martensson
|
|
def name
|
|
@name
|
|
end
|
|
end
|
|
end
|