mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	[ruby-core:33305] Reported by Aaron Patterson. Thanks!!! git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30113 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			269 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
require "rexml/parent"
 | 
						|
require "rexml/parseexception"
 | 
						|
require "rexml/namespace"
 | 
						|
require 'rexml/entity'
 | 
						|
require 'rexml/attlistdecl'
 | 
						|
require 'rexml/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
 | 
						|
      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
 | 
						|
      unless @children.empty?
 | 
						|
        output << ' ['
 | 
						|
        @children.each { |child|
 | 
						|
          output << "\n"
 | 
						|
          f.write( child, output )
 | 
						|
        }
 | 
						|
        output << "\n]"
 | 
						|
      end
 | 
						|
      output << STOP
 | 
						|
    end
 | 
						|
 | 
						|
    def context
 | 
						|
      @parent.context
 | 
						|
    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
 | 
						|
      notation = "<!NOTATION #{@name} #{@middle}"
 | 
						|
      notation << " #{@public.inspect}" if @public
 | 
						|
      notation << " #{@system.inspect}" 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
 |