r1287@bean:  ser | 2007-07-24 20:12:25 -0400
 Applied patch from Jeff Barczewski.  Note that this changes what the values of
 the name and IDs are from the previous behavior -- the values no longer include
 the quotes.  This is the correct behavior, so I'm leaving it in, but it is not
 backwards compatible.  Also fixes the serializer so that it outputs the doctype
 in a correct format (needed as a result of this change).

 Addresses ticket:92.
4 files changed, 76 insertions(+), 7 deletions(-)

M src/rexml/doctype.rb
M src/rexml/parsers/baseparser.rb
M src/rexml/parsers/sax2parser.rb
M test/sax.rb
M src/rexml/doctype.rb +2 -2
@@ 112,8 112,8 @@ module REXML
       output << ' '
       output << @name
       output << " #@external_id" if @external_id
-      output << " #@long_name" if @long_name
-      output << " #@uri" if @uri
+      output << " #{@long_name.inspect}" if @long_name
+      output << " #{@uri.inspect}" if @uri
       unless @children.empty?
         next_indent = indent + 1
         output << ' ['

          
M src/rexml/parsers/baseparser.rb +4 -4
@@ 53,7 53,7 @@ module REXML
       STANDALONE = /\bstandalone\s*=\s["'](.*?)['"]/um
 
       ENTITY_START = /^\s*<!ENTITY/
-      IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'].*?['"])?(\s+['"].*?["'])?/u
+      IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
       ELEMENTDECL_START = /^\s*<!ELEMENT/um
       ELEMENTDECL_PATTERN = /^\s*(<!ELEMENT.*?)>/um
       SYSTEMENTITY = /^\s*(%.*?;)\s*$/um

          
@@ 217,10 217,10 @@ module REXML
             close = md[2]
             identity =~ IDENTITY
             name = $1
-            raise REXML::ParseException("DOCTYPE is missing a name") if name.nil?
+            raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil?
             pub_sys = $2.nil? ? nil : $2.strip
-            long_name = $3.nil? ? nil : $3.strip
-            uri = $4.nil? ? nil : $4.strip
+            long_name = $4.nil? ? nil : $4.strip
+            uri = $6.nil? ? nil : $6.strip
             args = [ :start_doctype, name, pub_sys, long_name, uri ]
             if close == ">"
               @document_status = :after_doctype

          
M src/rexml/parsers/sax2parser.rb +3 -1
@@ 94,6 94,8 @@ module REXML
 					when :end_document
 						handle( :end_document )
 						break
+                                        when :start_doctype
+                                                handle( :doctype, *event[1..-1])
 					when :end_doctype
 						context = context[1]
 					when :start_element

          
@@ 167,7 169,7 @@ module REXML
           when :entitydecl
             @entities[ event[1] ] = event[2] if event.size == 3
 						handle( *event )
-					when :processing_instruction, :comment, :doctype, :attlistdecl, 
+					when :processing_instruction, :comment, :attlistdecl, 
 						:elementdecl, :cdata, :notationdecl, :xmldecl
 						handle( *event )
 					end

          
M test/sax.rb +67 -0
@@ 84,6 84,73 @@ class SAX2Tester < Test::Unit::TestCase
     assert_equal 1, end_document
   end
 
+
+
+  # used by test_simple_doctype_listener
+  # submitted by Jeff Barczewski
+  class SimpleDoctypeListener
+    include REXML::SAX2Listener
+    attr_reader :name, :pub_sys, :long_name, :uri
+
+    def initialize
+      @name = @pub_sys = @long_name = @uri = nil
+    end
+
+    def doctype(name, pub_sys, long_name, uri)
+      @name = name
+      @pub_sys = pub_sys
+      @long_name = long_name
+      @uri = uri
+    end
+  end
+
+  # test simple non-entity doctype in sax listener
+  # submitted by Jeff Barczewski
+  def test_simple_doctype_listener
+    xml = <<-END
+      <?xml version="1.0"?>
+      <!DOCTYPE greeting PUBLIC "Hello Greeting DTD" "http://foo/hello.dtd">
+      <greeting>Hello, world!</greeting>
+    END
+    parser = Parsers::SAX2Parser.new(xml)
+    dtl = SimpleDoctypeListener.new
+    parser.listen(dtl)
+    tname = nil
+    tpub_sys = nil
+    tlong_name = nil
+    turi = nil
+    parser.listen(:doctype) do |name, pub_sys, long_name, uri|
+      tname = name
+      tpub_sys = pub_sys
+      tlong_name = long_name
+      turi = uri
+    end
+    parser.parse
+    assert_equal 'greeting', tname, 'simple doctype block listener failed - incorrect name'
+    assert_equal 'PUBLIC', tpub_sys, 'simple doctype block listener failed - incorrect pub_sys'
+    assert_equal 'Hello Greeting DTD', tlong_name, 'simple doctype block listener failed - incorrect long_name'
+    assert_equal 'http://foo/hello.dtd', turi, 'simple doctype block listener failed - incorrect uri'
+    assert_equal 'greeting', dtl.name, 'simple doctype listener failed - incorrect name'
+    assert_equal 'PUBLIC', dtl.pub_sys, 'simple doctype listener failed - incorrect pub_sys'
+    assert_equal 'Hello Greeting DTD', dtl.long_name, 'simple doctype listener failed - incorrect long_name'
+    assert_equal 'http://foo/hello.dtd', dtl.uri, 'simple doctype listener failed - incorrect uri'
+  end
+
+  # test doctype with missing name, should throw ParseException
+  # submitted by Jeff Barczewseki
+  def test_doctype_with_mising_name_throws_exception
+    xml = <<-END
+      <?xml version="1.0"?>
+      <!DOCTYPE >
+      <greeting>Hello, world!</greeting>
+    END
+    parser = Parsers::SAX2Parser.new(xml)
+    assert_raise(REXML::ParseException, 'doctype missing name did not throw ParseException') do
+      parser.parse
+    end
+  end
+
+
   class KouListener
     include REXML::SAX2Listener
     attr_accessor :sdoc, :edoc