# MWDOM XMLWriter
# Copyright (c)2001, MoonWolf. All right reserved.
require "mwdom/visitor/domvisitor"

module MWDOM
    
  class XMLWriter < DOMVisitor
    def nscollect(doc)
      knownNS = {
        "http://www.w3.org/XML/1998/namespace" => "xml",
        "http://www.w3.org/1999/02/22-rdf-syntax-ns#" => "rdf",
        "http://purl.org/dc/elements/1.1/" => "dc",
        "http://www.w3.org/1999/xhtml" => "xhtml",
        "http://www.w3.org/2000/svg" => "svg",
        "http://www.w3.org/1999/XSL/Transform" => "xsl",
        "http://www.w3.org/TR/REC-html40" => "html",
      }
      pre = "ns1"
      namespaces = {}
      nodes = [doc]
      while node=nodes.pop
        ns = node.namespaceURI
        unless namespaces[ns]
          unless prefix=knownNS[ns]
            prefix=pre
            pre=pre.succ
          end
          namespaces[ns]=prefix
        end
        #
        if attrs = node.attributes
          i = 0
          j = attrs.length
          while i<j
            ns = attrs.item(i).namespaceURI
            unless namespaces[ns]
              unless prefix=knownNS[ns]
                prefix=pre
                pre=pre.succ
              end
              namespaces[ns]=prefix
            end
            i += 1
          end
        end
        #
        c = node.childNodes
        i = 0
        j = c.length
        while i<j
          n=c.item(i)
          nodes.push n if n.nodeType==ELEMENT_Node
          i += 1
        end
      end
      namespaces
    end
    
    
    def initialize(out)
      @out = out
      @ns  = nil
      @defaultns = nil
      
      @out << "<?xml version=\"1.0\"?>"
    end
    
    def visit_Document(node)
      @ns = nscollect(node)
      c = node.childNodes
      i = 0
      j = c.length
      while i<j
        n=c.item(i)
        if n.nodeType==ELEMENT_Node
          visit_RootElement(n)
        else
          visit(n)
        end
        i += 1
      end
    end
    
    def visit_DocumentType(node)
      @out << "<!DOCTYPE #{node.nodeName}"
      if node.publicId
        @out << " PUBLIC \"#{node.publicId}\""
        @out << " \"#{node.systemId}\"" if node.systemId
      elsif node.systemId
        @out << " SYSTEM \"#{node.systemId}\""
      end
      @out << ">"
      #visit_childNodes(node)
    end
    
    def visit_RootElement(node)
      @defaultns = node.namespaceURI
      namespaceURI = node.namespaceURI
      attr = ""
      
      #タグ名
      if namespaceURI==@defaultns
        name = node.localName
      else
        name = @ns[namespaceURI] + ":" + node.localName
      end
      #
      @out << "<#{name}"
      
      #名前空間宣言
      @out << " xmlns=\"#{@defaultns}\"" if @defaultns
      @ns.each {|namespace,prefix|
        @out << " xmlns:#{prefix}=\"#{namespace}\"" if namespace && prefix!="xml"
      }
      
      #属性
      c = node.attributes
      i = 0
      j = c.length
      while i<j
        n=c.item(i)
        
        #属性名
        if ns=n.namespaceURI
          attrname = @ns[ns] + ":" + n.localName
        else
          attrname = n.localName
        end
        @out << " " + attrname + '="'
        
        #属性値
        v = n.childNodes
        vi = 0
        vj = v.length
        while vi<vj
          visit(v.item(vi))
          vi += 1
        end
        
        @out << '"'
        i += 1
      end
      @out << ">"
      visit_childNodes(node)
      @out << "</#{name}>"
    end
    
    def visit_Element(node)
      namespaceURI = node.namespaceURI
      attr = ""
      #タグ名
      name = node.localName
      @out << "<#{name}"
      #属性
      c = node.attributes
      i = 0
      j = c.length
      while i<j
        n=c.item(i)
        
        #属性名
        if ns=n.namespaceURI
          attrname = @ns[ns] + ":" + n.localName
        else
          attrname = n.localName
        end
        @out << " " + attrname + '="'
        
        #属性値
        v = n.childNodes
        vi = 0
        vj = v.length
        while vi<vj
          visit(v.item(vi))
          vi += 1
        end
        
        @out << '"'
        i += 1
      end
      if namespaceURI!=@defaultns
        @out << " xmlns=\"#{namespaceURI}\""
        @out << ">"
        ns=@defaultns
        @defaultns=namespaceURI
        visit_childNodes(node)
        @defaultns=ns
        @out << "</#{name}>"
      else
        @out << ">"
        visit_childNodes(node)
        @out << "</#{name}>"
      end
    end
    
    def visit_Text(node)
      @out << MWDOM::escape(node.data)
    end
    
    def visit_EntityReference(node)
      @out << "&#{node.nodeName};"
    end
    
    def visit_Comment(node)
      @out << "<!--#{node.data}-->"
    end
    
    def visit_ProcessingInstruction(node)
      @out << "<?#{node.nodeName} #{node.nodeValue}?>"
    end
    
    def visit_CDATASection(node)
      @out << "<![CDATA[" + node.nodeValue + "]]>"
    end
    
    def visit_DocumentFragment(node)
      visit_childNodes(node)
    end
    
    def visit_Entity(node)
    end
    
    def visit_Notation(node)
    end
    
    def visit_Attr(node)
    end
  end
end

