1require "rexml/parent" 2require "rexml/parseexception" 3require "rexml/namespace" 4require 'rexml/entity' 5require 'rexml/attlistdecl' 6require 'rexml/xmltokens' 7 8module REXML 9 # Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE 10 # ... >. DOCTYPES can be used to declare the DTD of a document, as well as 11 # being used to declare entities used in the document. 12 class DocType < Parent 13 include XMLTokens 14 START = "<!DOCTYPE" 15 STOP = ">" 16 SYSTEM = "SYSTEM" 17 PUBLIC = "PUBLIC" 18 DEFAULT_ENTITIES = { 19 'gt'=>EntityConst::GT, 20 'lt'=>EntityConst::LT, 21 'quot'=>EntityConst::QUOT, 22 "apos"=>EntityConst::APOS 23 } 24 25 # name is the name of the doctype 26 # external_id is the referenced DTD, if given 27 attr_reader :name, :external_id, :entities, :namespaces 28 29 # Constructor 30 # 31 # dt = DocType.new( 'foo', '-//I/Hate/External/IDs' ) 32 # # <!DOCTYPE foo '-//I/Hate/External/IDs'> 33 # dt = DocType.new( doctype_to_clone ) 34 # # Incomplete. Shallow clone of doctype 35 # 36 # +Note+ that the constructor: 37 # 38 # Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) ) 39 # 40 # is _deprecated_. Do not use it. It will probably disappear. 41 def initialize( first, parent=nil ) 42 @entities = DEFAULT_ENTITIES 43 @long_name = @uri = nil 44 if first.kind_of? String 45 super() 46 @name = first 47 @external_id = parent 48 elsif first.kind_of? DocType 49 super( parent ) 50 @name = first.name 51 @external_id = first.external_id 52 elsif first.kind_of? Array 53 super( parent ) 54 @name = first[0] 55 @external_id = first[1] 56 @long_name = first[2] 57 @uri = first[3] 58 elsif first.kind_of? Source 59 super( parent ) 60 parser = Parsers::BaseParser.new( first ) 61 event = parser.pull 62 if event[0] == :start_doctype 63 @name, @external_id, @long_name, @uri, = event[1..-1] 64 end 65 else 66 super() 67 end 68 end 69 70 def node_type 71 :doctype 72 end 73 74 def attributes_of element 75 rv = [] 76 each do |child| 77 child.each do |key,val| 78 rv << Attribute.new(key,val) 79 end if child.kind_of? AttlistDecl and child.element_name == element 80 end 81 rv 82 end 83 84 def attribute_of element, attribute 85 att_decl = find do |child| 86 child.kind_of? AttlistDecl and 87 child.element_name == element and 88 child.include? attribute 89 end 90 return nil unless att_decl 91 att_decl[attribute] 92 end 93 94 def clone 95 DocType.new self 96 end 97 98 # output:: 99 # Where to write the string 100 # indent:: 101 # An integer. If -1, no indentation will be used; otherwise, the 102 # indentation will be this number of spaces, and children will be 103 # indented an additional amount. 104 # transitive:: 105 # Ignored 106 # ie_hack:: 107 # Ignored 108 def write( output, indent=0, transitive=false, ie_hack=false ) 109 f = REXML::Formatters::Default.new 110 indent( output, indent ) 111 output << START 112 output << ' ' 113 output << @name 114 output << " #@external_id" if @external_id 115 output << " #{@long_name.inspect}" if @long_name 116 output << " #{@uri.inspect}" if @uri 117 unless @children.empty? 118 output << ' [' 119 @children.each { |child| 120 output << "\n" 121 f.write( child, output ) 122 } 123 output << "\n]" 124 end 125 output << STOP 126 end 127 128 def context 129 @parent.context 130 end 131 132 def entity( name ) 133 @entities[name].unnormalized if @entities[name] 134 end 135 136 def add child 137 super(child) 138 @entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES 139 @entities[ child.name ] = child if child.kind_of? Entity 140 end 141 142 # This method retrieves the public identifier identifying the document's 143 # DTD. 144 # 145 # Method contributed by Henrik Martensson 146 def public 147 case @external_id 148 when "SYSTEM" 149 nil 150 when "PUBLIC" 151 strip_quotes(@long_name) 152 end 153 end 154 155 # This method retrieves the system identifier identifying the document's DTD 156 # 157 # Method contributed by Henrik Martensson 158 def system 159 case @external_id 160 when "SYSTEM" 161 strip_quotes(@long_name) 162 when "PUBLIC" 163 @uri.kind_of?(String) ? strip_quotes(@uri) : nil 164 end 165 end 166 167 # This method returns a list of notations that have been declared in the 168 # _internal_ DTD subset. Notations in the external DTD subset are not 169 # listed. 170 # 171 # Method contributed by Henrik Martensson 172 def notations 173 children().select {|node| node.kind_of?(REXML::NotationDecl)} 174 end 175 176 # Retrieves a named notation. Only notations declared in the internal 177 # DTD subset can be retrieved. 178 # 179 # Method contributed by Henrik Martensson 180 def notation(name) 181 notations.find { |notation_decl| 182 notation_decl.name == name 183 } 184 end 185 186 private 187 188 # Method contributed by Henrik Martensson 189 def strip_quotes(quoted_string) 190 quoted_string =~ /^[\'\"].*[\'\"]$/ ? 191 quoted_string[1, quoted_string.length-2] : 192 quoted_string 193 end 194 end 195 196 # We don't really handle any of these since we're not a validating 197 # parser, so we can be pretty dumb about them. All we need to be able 198 # to do is spew them back out on a write() 199 200 # This is an abstract class. You never use this directly; it serves as a 201 # parent class for the specific declarations. 202 class Declaration < Child 203 def initialize src 204 super() 205 @string = src 206 end 207 208 def to_s 209 @string+'>' 210 end 211 212 # == DEPRECATED 213 # See REXML::Formatters 214 # 215 def write( output, indent ) 216 output << to_s 217 end 218 end 219 220 public 221 class ElementDecl < Declaration 222 def initialize( src ) 223 super 224 end 225 end 226 227 class ExternalEntity < Child 228 def initialize( src ) 229 super() 230 @entity = src 231 end 232 def to_s 233 @entity 234 end 235 def write( output, indent ) 236 output << @entity 237 end 238 end 239 240 class NotationDecl < Child 241 attr_accessor :public, :system 242 def initialize name, middle, pub, sys 243 super(nil) 244 @name = name 245 @middle = middle 246 @public = pub 247 @system = sys 248 end 249 250 def to_s 251 notation = "<!NOTATION #{@name} #{@middle}" 252 notation << " #{@public.inspect}" if @public 253 notation << " #{@system.inspect}" if @system 254 notation << ">" 255 notation 256 end 257 258 def write( output, indent=-1 ) 259 output << to_s 260 end 261 262 # This method retrieves the name of the notation. 263 # 264 # Method contributed by Henrik Martensson 265 def name 266 @name 267 end 268 end 269end 270