<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/nokogiri/xml/sax/legacy_handlers.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,13 @@
 #include &lt;nokogiri.h&gt;
 
+#define STRING_OR_NULL(str) \
+  ({ \
+   RTEST(str) ? StringValuePtr(str) : NULL \
+  })
+
+#define RBSTR_OR_QNIL(_str, rb_enc) \
+  (_str ? NOKOGIRI_STR_NEW2(_str, STRING_OR_NULL(rb_enc)) : Qnil)
+
 /*
  * call-seq:
  *  parse_memory(data)
@@ -89,7 +97,7 @@ static void start_element(void * ctx, const xmlChar *name, const xmlChar **atts)
   if(atts) {
     while((attr = atts[i]) != NULL) {
       rb_funcall(attributes, rb_intern(&quot;&lt;&lt;&quot;), 1,
-          NOKOGIRI_STR_NEW2(attr, RTEST(enc) ? StringValuePtr(enc) : NULL)
+          NOKOGIRI_STR_NEW2(attr, STRING_OR_NULL(enc))
       );
       i++;
     }
@@ -98,7 +106,7 @@ static void start_element(void * ctx, const xmlChar *name, const xmlChar **atts)
   rb_funcall( doc,
               rb_intern(&quot;start_element&quot;),
               2,
-              NOKOGIRI_STR_NEW2(name, RTEST(enc) ? StringValuePtr(enc) : NULL),
+              NOKOGIRI_STR_NEW2(name, STRING_OR_NULL(enc)),
               attributes
   );
 }
@@ -109,19 +117,51 @@ static void end_element(void * ctx, const xmlChar *name)
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
   rb_funcall(doc, rb_intern(&quot;end_element&quot;), 1,
-      NOKOGIRI_STR_NEW2(name, RTEST(enc) ? StringValuePtr(enc) : NULL)
+      NOKOGIRI_STR_NEW2(name, STRING_OR_NULL(enc))
   );
 }
 
-/**
- * start_element_ns was borrowed heavily from libxml-ruby. 
- */
+static VALUE attributes_as_list(
+  VALUE self,
+  int nb_attributes,
+  const xmlChar ** attributes)
+{
+  VALUE list = rb_ary_new2(nb_attributes);
+  VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
+
+  VALUE attr_klass = rb_const_get(cNokogiriXmlSaxParser, rb_intern(&quot;Attribute&quot;));
+  if (attributes) {
+    /* Each attribute is an array of [localname, prefix, URI, value, end] */
+    int i;
+    for (i = 0; i &lt; nb_attributes * 5; i += 5) {
+      VALUE attribute = rb_funcall(attr_klass, rb_intern(&quot;new&quot;), 4,
+        /* localname */
+        RBSTR_OR_QNIL(attributes[i + 0], enc),
+
+        /* prefix */
+        RBSTR_OR_QNIL(attributes[i + 1], enc),
+
+        /* URI */
+        RBSTR_OR_QNIL(attributes[i + 2], enc),
+
+        /* value */
+        NOKOGIRI_STR_NEW((const char*)attributes[i+3],
+          (attributes[i+4] - attributes[i+3]),
+          STRING_OR_NULL(enc))
+      );
+      rb_ary_push(list, attribute);
+    }
+  }
+
+  return list;
+}
+
 static void
 start_element_ns (
   void * ctx,
   const xmlChar * localname,
   const xmlChar * prefix,
-  const xmlChar * URI,
+  const xmlChar * uri,
   int nb_namespaces,
   const xmlChar ** namespaces,
   int nb_attributes,
@@ -132,52 +172,42 @@ start_element_ns (
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
 
-  VALUE attrHash = rb_hash_new();
-  VALUE nsHash = rb_hash_new();
+  VALUE attribute_list = attributes_as_list(self, nb_attributes, attributes);
 
-  if (attributes)
-  {
-    /* Each attribute is an array of [localname, prefix, URI, value, end] */
-    int i;
-    for (i = 0; i &lt; nb_attributes * 5; i += 5)
-    {
-      rb_hash_aset( attrHash,
-                    NOKOGIRI_STR_NEW2((const char*)attributes[i+0], RTEST(enc) ? StringValuePtr(enc) : NULL),
-                    NOKOGIRI_STR_NEW((const char*)attributes[i+3], (attributes[i+4] - attributes[i+3]), RTEST(enc) ? StringValuePtr(enc) : NULL));
-    }
-  }
+  VALUE ns_list = rb_ary_new2(nb_namespaces);
 
-  if (namespaces)
-  {
+  if (namespaces) {
     int i;
     for (i = 0; i &lt; nb_namespaces * 2; i += 2)
     {
-      rb_hash_aset( nsHash,
-                    namespaces[i+0] ? NOKOGIRI_STR_NEW2((const char*)namespaces[i+0], RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil,
-                    namespaces[i+1] ? NOKOGIRI_STR_NEW2((const char*)namespaces[i+1], RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil);
+      rb_ary_push(ns_list,
+        rb_ary_new3(2,
+          RBSTR_OR_QNIL(namespaces[i + 0], enc),
+          RBSTR_OR_QNIL(namespaces[i + 1], enc)
+        )
+      );
     }
   }
 
   rb_funcall( doc,
-              rb_intern(&quot;start_element_ns&quot;),
+              rb_intern(&quot;start_element_namespace&quot;),
               5,
-              NOKOGIRI_STR_NEW2(localname, RTEST(enc) ? StringValuePtr(enc) : NULL),
-              attrHash,
-              prefix ? NOKOGIRI_STR_NEW2(prefix, RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil,
-              URI ? NOKOGIRI_STR_NEW2(URI, RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil,
-              nsHash
+              NOKOGIRI_STR_NEW2(localname, STRING_OR_NULL(enc)),
+              attribute_list,
+              RBSTR_OR_QNIL(prefix, enc),
+              RBSTR_OR_QNIL(uri, enc),
+              ns_list
   );
 
-  /* Call start element if it's there' */
-  if (rb_respond_to(doc, rb_intern(&quot;start_element&quot;)))
-  {
-    VALUE name =
-      NOKOGIRI_STR_NEW2(localname, RTEST(enc) ? StringValuePtr(enc) : NULL);
-    VALUE attrArray = rb_funcall(attrHash, rb_intern(&quot;to_a&quot;), 0);
-    attrArray = rb_funcall(attrArray, rb_intern(&quot;flatten&quot;), 0);
-    rb_funcall(doc, rb_intern(&quot;start_element&quot;), 2, name, attrArray);
-  }
-
+  rb_funcall( self,
+              rb_intern(&quot;start_element_namespace&quot;),
+              5,
+              NOKOGIRI_STR_NEW2(localname, STRING_OR_NULL(enc)),
+              attribute_list,
+              RBSTR_OR_QNIL(prefix, enc),
+              RBSTR_OR_QNIL(uri, enc),
+              ns_list
+  );
 }
 
 /**
@@ -188,26 +218,23 @@ end_element_ns (
   void * ctx,
   const xmlChar * localname,
   const xmlChar * prefix,
-  const xmlChar * URI)
+  const xmlChar * uri)
 {
   VALUE self = (VALUE)ctx;
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
 
-  rb_funcall(doc, rb_intern(&quot;end_element_ns&quot;), 3, 
-             NOKOGIRI_STR_NEW2(localname, RTEST(enc) ? StringValuePtr(enc) : NULL),
-             prefix ? NOKOGIRI_STR_NEW2(prefix, RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil,
-             URI ? NOKOGIRI_STR_NEW2(URI, RTEST(enc) ? StringValuePtr(enc) : NULL) : Qnil
+  rb_funcall(doc, rb_intern(&quot;end_element_namespace&quot;), 3, 
+    NOKOGIRI_STR_NEW2(localname, STRING_OR_NULL(enc)),
+    RBSTR_OR_QNIL(prefix, enc),
+    RBSTR_OR_QNIL(uri, enc)
   );
 
-  /* Call end element for old-times sake */
-  if (rb_respond_to(doc, rb_intern(&quot;end_element&quot;)))
-  {
-    VALUE name =
-      NOKOGIRI_STR_NEW2(localname, RTEST(enc) ? StringValuePtr(enc) : NULL);
-    rb_funcall(doc, rb_intern(&quot;end_element&quot;), 1, name);
-  }
-
+  rb_funcall(self, rb_intern(&quot;end_element_namespace&quot;), 3, 
+    NOKOGIRI_STR_NEW2(localname, STRING_OR_NULL(enc)),
+    RBSTR_OR_QNIL(prefix, enc),
+    RBSTR_OR_QNIL(uri, enc)
+  );
 }
 
 static void characters_func(void * ctx, const xmlChar * ch, int len)
@@ -215,7 +242,7 @@ static void characters_func(void * ctx, const xmlChar * ch, int len)
   VALUE self = (VALUE)ctx;
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
-  VALUE str = NOKOGIRI_STR_NEW(ch, len, RTEST(enc) ? StringValuePtr(enc):NULL);
+  VALUE str = NOKOGIRI_STR_NEW(ch, len, STRING_OR_NULL(enc));
   rb_funcall(doc, rb_intern(&quot;characters&quot;), 1, str);
 }
 
@@ -224,7 +251,7 @@ static void comment_func(void * ctx, const xmlChar * value)
   VALUE self = (VALUE)ctx;
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
-  VALUE str = NOKOGIRI_STR_NEW2(value, RTEST(enc) ? StringValuePtr(enc):NULL);
+  VALUE str = NOKOGIRI_STR_NEW2(value, STRING_OR_NULL(enc));
   rb_funcall(doc, rb_intern(&quot;comment&quot;), 1, str);
 }
 
@@ -241,7 +268,7 @@ static void warning_func(void * ctx, const char *msg, ...)
   va_end(args);
 
   rb_funcall(doc, rb_intern(&quot;warning&quot;), 1,
-      NOKOGIRI_STR_NEW2(message, RTEST(enc) ? StringValuePtr(enc) : NULL)
+      NOKOGIRI_STR_NEW2(message, STRING_OR_NULL(enc))
   );
   free(message);
 }
@@ -259,7 +286,7 @@ static void error_func(void * ctx, const char *msg, ...)
   va_end(args);
 
   rb_funcall(doc, rb_intern(&quot;error&quot;), 1,
-      NOKOGIRI_STR_NEW2(message, RTEST(enc) ? StringValuePtr(enc) : NULL)
+      NOKOGIRI_STR_NEW2(message, STRING_OR_NULL(enc))
   );
   free(message);
 }
@@ -270,7 +297,7 @@ static void cdata_block(void * ctx, const xmlChar * value, int len)
   VALUE MAYBE_UNUSED(enc) = rb_iv_get(self, &quot;@encoding&quot;);
   VALUE doc = rb_funcall(self, rb_intern(&quot;document&quot;), 0);
   VALUE string =
-    NOKOGIRI_STR_NEW(value, len, RTEST(enc) ? StringValuePtr(enc) : NULL);
+    NOKOGIRI_STR_NEW(value, len, STRING_OR_NULL(enc));
   rb_funcall(doc, rb_intern(&quot;cdata_block&quot;), 1, string);
 }
 </diff>
      <filename>ext/nokogiri/xml_sax_parser.c</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,4 @@
 require 'nokogiri/xml/sax/document'
+require 'nokogiri/xml/sax/legacy_handlers'
 require 'nokogiri/xml/sax/parser'
 require 'nokogiri/xml/sax/push_parser'</diff>
      <filename>lib/nokogiri/xml/sax.rb</filename>
    </modified>
    <modified>
      <diff>@@ -93,11 +93,11 @@ module Nokogiri
         ###
         # Called at the beginning of an element
         # +name+ is the element name
-        # +attrs+ is a hash of attributes
+        # +attrs+ is a list of attributes
         # +prefix+ is the namespace prefix for the element
         # +uri+ is the associated namespace URI
         # +ns+ is a hash of namespace prefix:urls associated with the element
-        def start_element_ns name, attrs = {}, prefix = nil, uri = nil, ns = {}
+        def start_element_namespace name, attrs = {}, prefix = nil, uri = nil, ns = {}
         end
 
         ###
@@ -105,7 +105,7 @@ module Nokogiri
         # +name+ is the element's name
         # +prefix+ is the namespace prefix associated with the element
         # +uri+ is the associated namespace URI
-        def end_element_ns name, prefix = nil, uri = nil
+        def end_element_namespace name, prefix = nil, uri = nil
         end
 
         ###</diff>
      <filename>lib/nokogiri/xml/sax/document.rb</filename>
    </modified>
    <modified>
      <diff>@@ -30,6 +30,9 @@ module Nokogiri
       # For more information about SAX parsers, see Nokogiri::XML::SAX.  Also
       # see Nokogiri::XML::SAX::Document for the available events.
       class Parser
+        class Attribute &lt; Struct.new(:localname, :prefix, :uri, :value)
+        end
+
         # Encodinds this parser supports
         ENCODINGS = {
           'NONE'        =&gt; 0, # No char encoding detected
@@ -67,6 +70,7 @@ module Nokogiri
         def initialize(doc = Nokogiri::XML::SAX::Document.new, encoding = 'ASCII')
           @encoding = encoding
           @document = doc
+          @warned   = false
         end
 
         ###
@@ -95,6 +99,9 @@ module Nokogiri
           raise Errno::EISDIR if File.directory?(filename)
           native_parse_file filename
         end
+
+        private
+        include Nokogiri::XML::SAX::LegacyHandlers
       end
     end
   end</diff>
      <filename>lib/nokogiri/xml/sax/parser.rb</filename>
    </modified>
    <modified>
      <diff>@@ -54,6 +54,9 @@ module Nokogiri
         def finish
           write '', true
         end
+
+        private
+        include Nokogiri::XML::SAX::LegacyHandlers
       end
     end
   end</diff>
      <filename>lib/nokogiri/xml/sax/push_parser.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,7 +19,7 @@ module XSD # :nodoc:
     #   require 'xsd/xmlparser/nokogiri'
     #   
     #   obj = AvlPortType.new
-    #   obj.getLatestByRoute(obj.getAgencies.item, 8).item.each do |bus|
+    #   obj.getLatestByRoute(obj.getAgencies.first, 8).each do |bus|
     #     p &quot;#{bus.routeID}, #{bus.longitude}, #{bus.latitude}&quot;
     #   end
     #
@@ -62,7 +62,7 @@ module XSD # :nodoc:
         characters string
       end
 
-      %w{ start_document start_element_ns end_element_ns end_document comment }.each do |name|
+      %w{ start_document start_element_namespace end_element_namespace end_document comment }.each do |name|
         class_eval %{ def #{name}(*args); end }
       end
       add_factory(self)</diff>
      <filename>lib/xsd/xmlparser/nokogiri.rb</filename>
    </modified>
    <modified>
      <diff>@@ -56,10 +56,10 @@ module Nokogiri
   module SAX
     class TestCase &lt; Nokogiri::TestCase
       class Doc &lt; XML::SAX::Document
-        attr_reader :start_elements, :start_elements_ns, :start_document_called
-        attr_reader :end_elements, :end_elements_ns, :end_document_called
-        attr_reader :data, :comments, :cdata_blocks
-        attr_reader :errors, :warnings
+        attr_reader :start_elements, :start_document_called
+        attr_reader :end_elements, :end_document_called
+        attr_reader :data, :comments, :cdata_blocks, :start_elements_namespace
+        attr_reader :errors, :warnings, :end_elements_namespace
 
         def start_document
           @start_document_called = true
@@ -86,8 +86,8 @@ module Nokogiri
           super
         end
 
-        def start_element_ns *args
-          (@start_elements_ns ||= []) &lt;&lt; args
+        def start_element_namespace *args
+          (@start_elements_namespace ||= []) &lt;&lt; args
           super
         end
 
@@ -96,8 +96,8 @@ module Nokogiri
           super
         end
 
-        def end_element_ns *args
-          (@end_elements_ns ||= []) &lt;&lt; args
+        def end_element_namespace *args
+          (@end_elements_namespace ||= []) &lt;&lt; args
           super
         end
 </diff>
      <filename>test/helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,15 +9,86 @@ module Nokogiri
           @parser = XML::SAX::Parser.new(Doc.new)
         end
 
+        def test_namespace_declaration_order_is_saved
+          @parser.parse &lt;&lt;-eoxml
+&lt;root xmlns:foo='http://foo.example.com/' xmlns='http://example.com/'&gt;
+  &lt;a foo:bar='hello' /&gt;
+&lt;/root&gt;
+          eoxml
+          assert_equal 2, @parser.document.start_elements_namespace.length
+          el = @parser.document.start_elements_namespace.first
+          namespaces = el.last
+          assert_equal ['foo', 'http://foo.example.com/'], namespaces.first
+          assert_equal [nil, 'http://example.com/'], namespaces.last
+        end
+
         def test_bad_document_calls_error_handler
           @parser.parse('&lt;foo&gt;&lt;bar&gt;&lt;/foo&gt;')
           assert @parser.document.errors
           assert @parser.document.errors.length &gt; 0
         end
 
+        def test_namespace_are_super_fun_to_parse
+          @parser.parse &lt;&lt;-eoxml
+&lt;root xmlns:foo='http://foo.example.com/'&gt;
+  &lt;a foo:bar='hello' /&gt;
+  &lt;b xmlns:foo='http://bar.example.com/'&gt;
+    &lt;a foo:bar='hello' /&gt;
+  &lt;/b&gt;
+  &lt;foo:bar&gt;hello world&lt;/foo:bar&gt;
+&lt;/root&gt;
+          eoxml
+          assert @parser.document.start_elements_namespace.length &gt; 0
+          el = @parser.document.start_elements_namespace[1]
+          assert_equal 'a', el.first
+          assert_equal 1, el[1].length
+
+          attribute = el[1].first
+          assert_equal 'bar', attribute.localname
+          assert_equal 'foo', attribute.prefix
+          assert_equal 'hello', attribute.value
+          assert_equal 'http://foo.example.com/', attribute.uri
+        end
+
+        def test_sax_v1_namespace_attribute_declarations
+          @parser.parse &lt;&lt;-eoxml
+&lt;root xmlns:foo='http://foo.example.com/' xmlns='http://example.com/'&gt;
+  &lt;a foo:bar='hello' /&gt;
+  &lt;b xmlns:foo='http://bar.example.com/'&gt;
+    &lt;a foo:bar='hello' /&gt;
+  &lt;/b&gt;
+  &lt;foo:bar&gt;hello world&lt;/foo:bar&gt;
+&lt;/root&gt;
+          eoxml
+          assert @parser.document.start_elements.length &gt; 0
+          elm = @parser.document.start_elements.first
+          assert_equal 'root', elm.first
+          assert elm[1].include? ['xmlns:foo', 'http://foo.example.com/']
+          assert elm[1].include? ['xmlns', 'http://example.com/']
+        end
+
+        def test_sax_v1_namespace_nodes
+          @parser.parse &lt;&lt;-eoxml
+&lt;root xmlns:foo='http://foo.example.com/' xmlns='http://example.com/'&gt;
+  &lt;a foo:bar='hello' /&gt;
+  &lt;b xmlns:foo='http://bar.example.com/'&gt;
+    &lt;a foo:bar='hello' /&gt;
+  &lt;/b&gt;
+  &lt;foo:bar&gt;hello world&lt;/foo:bar&gt;
+&lt;/root&gt;
+          eoxml
+          assert_equal 5, @parser.document.start_elements.length
+          assert @parser.document.start_elements.map { |se|
+            se.first
+          }.include? 'foo:bar'
+          assert @parser.document.end_elements.map { |se|
+            se.first
+          }.include? 'foo:bar'
+        end
+
         def test_start_is_called_without_namespace
           @parser.parse('&lt;foo:f&gt;&lt;bar&gt;&lt;/foo:f&gt;')
-          assert_equal ['f', 'bar'],
+          assert_equal ['foo:f', 'bar'],
             @parser.document.start_elements.map { |x| x.first }
         end
 </diff>
      <filename>test/xml/sax/test_parser.rb</filename>
    </modified>
    <modified>
      <diff>@@ -43,12 +43,16 @@ module Nokogiri
             &lt;stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' size='large'&gt;&lt;/stream:stream&gt;
           eoxml
 
-          assert_equal [[ 'stream',
-                          {'version' =&gt; '1.0', 'size' =&gt; 'large'},
-                          'stream',
-                          'http://etherx.jabber.org/streams',
-                          {nil =&gt; 'jabber:client', 'stream' =&gt; 'http://etherx.jabber.org/streams'}]],
-            @parser.document.start_elements_ns
+          assert_equal 1, @parser.document.start_elements_namespace.length
+          el = @parser.document.start_elements_namespace.first
+
+          assert_equal 'stream', el.first
+          assert_equal 2, el[1].length
+          assert_equal [['version', '1.0'], ['size', 'large']],
+            el[1].map { |x| [x.localname, x.value] }
+
+          assert_equal 'stream', el[2]
+          assert_equal 'http://etherx.jabber.org/streams', el[3]
           @parser.finish
         end
 
@@ -58,7 +62,7 @@ module Nokogiri
           eoxml
 
           assert_equal [['stream', 'stream', 'http://etherx.jabber.org/streams']],
-            @parser.document.end_elements_ns
+            @parser.document.end_elements_namespace
           @parser.finish
         end
 </diff>
      <filename>test/xml/sax/test_push_parser.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>a69f9957de45845f5bacb721e7e10b8acd818431</id>
    </parent>
  </parents>
  <author>
    <name>Aaron Patterson</name>
    <email>aaron.patterson@gmail.com</email>
  </author>
  <url>http://github.com/tenderlove/nokogiri/commit/507b912cc4cad68a98d1a3ff37195d180b08c71a</url>
  <id>507b912cc4cad68a98d1a3ff37195d180b08c71a</id>
  <committed-date>2009-06-20T19:33:02-07:00</committed-date>
  <authored-date>2009-06-20T19:33:02-07:00</authored-date>
  <message>Squashed commit of the following:

commit cb697a95616d240d2a7d81c284e920753f890231
Author: Aaron Patterson &lt;aaron.patterson@gmail.com&gt;
Date:   Sat Jun 20 19:32:40 2009 -0700

    making sure namespace declaration order is preserved

commit 51e1d483215e98fa17217cf86683973f1b9ab896
Author: Aaron Patterson &lt;aaron.patterson@gmail.com&gt;
Date:   Sat Jun 20 19:00:48 2009 -0700

    making sax v1 parser work properly

commit d4f3091b31a03a78067a67b8dd0e50d83a2500c8
Author: Aaron Patterson &lt;aaron.patterson@gmail.com&gt;
Date:   Sat Jun 20 17:54:36 2009 -0700

    intermediate fixes</message>
  <tree>d54cc5bb0080fe4f4a73bb16228ad74766d4cc0b</tree>
  <committer>
    <name>Aaron Patterson</name>
    <email>aaron.patterson@gmail.com</email>
  </committer>
</commit>
