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 . 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 = "" 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' ) # # # dt = DocType.new( doctype_to_clone ) # # Incomplete. Shallow clone of doctype # source = Source.new( '' ) # dt = DocType.new( source ) # # # dt = DocType.new( source, some_document ) # # Creates a doctype, and adds to the supplied document 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] 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 indenting will be used; otherwise, the # indentation will be this number of spaces, and children will be # indented an additional amount. # transitive:: # If transitive is true and indent is >= 0, then the output will be # pretty-printed in such a way that the added whitespace does not affect # the absolute *value* of the document -- that is, it leaves the value # and number of Text nodes in the document unchanged. # ie_hack:: # Internet Explorer is the worst piece of crap to have ever been # written, with the possible exception of Windows itself. Since IE is # unable to parse proper XML, we have to provide a hack to generate XML # that IE's limited abilities can handle. This hack inserts a space # before the /> on empty tags. # def write( output, indent=0, transitive=false, ie_hack=false ) indent( output, indent ) output << START output << ' ' output << @name output << " #@external_id" if @external_id output << " #@long_name" if @long_name output << " #@uri" if @uri unless @children.empty? next_indent = indent + 1 output << ' [' child = nil # speed @children.each { |child| output << "\n" child.write( output, next_indent ) } output << "\n" #output << ' '*next_indent output << "]" 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 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 def write( output, indent ) output << (' '*indent) if indent > 0 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 output << "\n" end end class NotationDecl < Child def initialize name, middle, rest @name = name @middle = middle @rest = rest end def to_s "" end def write( output, indent=-1 ) output << (' '*indent) if indent > 0 output << to_s end end end