########################################################################
#
# File Name:            SaxWriter.py
#
# Documentation:        http://docs.4suite.org/4XSLT/SaxWriter.py.html
#
"""
Implement the core SAX Writer for XSLT processor output
WWW: http://4suite.org/4XSLT        e-mail: support@4suite.org

Copyright (c) 1999-2001 Fourthought Inc., USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

import string, os
import xml.dom.ext
from xml.xslt import XSL_NAMESPACE, TextSax
from xml.dom.ext.reader import Sax, Sax2, HtmlSax
from xml.dom import implementation
from xml.xpath import Util
from xml.dom import XMLNS_NAMESPACE
from xml.xslt import OutputParameters


class ElementData:
    def __init__(self, name, attrs, extraNss=None):
        self.name = name
        self.attrs = attrs
        self.extraNss = extraNss or {}


class SaxWriter:
    def __init__(self, outputParams,saxHandler=None, fragment=0):
        self.__fragment = fragment
        self.__firstElementFlag = 1
        self.__orphanedNodes = []
        self.__saxHandlerClass = None
        self.__saxHandler = None
        self.__stateStack = []
        self.__currElement = None
        self.__currText = ''
        self._outputParams = outputParams or OutputParameters()
        if saxHandler:
            self.__saxHandler = saxHandler
        elif self.__outputParams.method == 'xml':
            self.__initSaxHandler(Sax2.XmlDomGenerator)
            if self.__outputParams.omitXmlDeclaration in [None, 'no']:
                self.__saxHandler.xmlDecl(
                    self.__outputParams.version,
                    self.__outputParams.encoding,
                    self.__outputParams.standalone
                    )
        elif self.__outputParams.method == 'html':
            self.__initSaxHandler(HtmlSax.HtmlDomGenerator)
        elif self.__outputParams.method == 'text':
            self.__initSaxHandler(TextSax.TextGenerator)

    def startDocument(self):
        return

    def endDocument(self):
        return
    
    def complete(self):
        return self.__saxHandler and self.__saxHandler.getRootNode() or None

    def getResult(self):
        self.__completeTextNode()
        return self.__saxHandler.getRootNode()

    def __initSaxHandler(self, saxHandlerClass):
        self.__saxHandlerClass = saxHandlerClass
        self.__saxHandler = saxHandlerClass(keepAllWs=1)
        for o_node in self.__orphanedNodes:
            if o_node[0] == 'pi':
                self.__saxHandler.processingInstruction(o_node[1], o_node[2])
            elif o_node[0] == 'comment':
                self.__saxHandler.comment(o_node[1])
        del self.__orphanedNodes
        return

    def __initSax2Doc(self, doctype):
        self.__firstElementFlag = 0
        if not self.__fragment:
            if not self.__saxHandler:
                self.__initSaxHandler(Sax2.XmlDomGenerator)
                if self.__outputParams.omitXmlDeclaration in [None, 'no']:
                    self.__saxHandler.xmlDecl(
                        self.__outputParams.version,
                        self.__outputParams.encoding,
                        self.__outputParams.standalone
                        )
            self.__saxHandler.startDTD(doctype, self.__outputParams.doctypeSystem, self.__outputParams.doctypePublic)
            self.__saxHandler.endDTD()
        return

    def __initHtmlSaxDoc(self, doctype):
        self.__firstElementFlag = 0
        if not self.__saxHandler:
            self.__initSaxHandler(HtmlSax.HtmlDomGenerator)
        #self.__saxHandler._4dom_startDTD(doctype, self.__outputParams.doctypeSystem, self.__outputParams.doctypePublic)
        #self.__saxHandler.endDTD()

    def __completeTextNode(self):
        #FIXME: This does not allow multiple root nodes, which is required to be supported
        if self.__currText:
            if string.strip(self.__currText):
                self.__saxHandler.characters(
                    self.__currText,
                    0,
                    len(self.__currText)
                    )
            else:
                self.__saxHandler.ignorableWhitespace(
                    self.__currText,
                    0,
                    len(self.__currText)
                    )
            self.__currText = ''
        return

    def startElement(self, name, namespace='', extraNss=None):
        extraNss = extraNss or {}
        attrs = {}
        if self.__firstElementFlag:
            if not self.__outputParams.method:
                if not namespace and string.upper(name) == 'HTML':
                    self.__outputParams.method = 'html'
                else:
                    self.__outputParams.method = 'xml'
            if self.__outputParams.method == 'xml':
                self.__initSax2Doc(name)
            else:
                self.__initHtmlSaxDoc(name)
            self.__firstElementFlag = 0
        self.__completeTextNode()
        if self.__currElement:
            self.__saxHandler.startElement(self.__currElement.name, self.__currElement.attrs)
            self.__currElement = None
        self.__currElement = ElementData(name, attrs, extraNss)
        if self.__outputParams.method == 'xml':
            if namespace:
                (prefix, local) = xml.dom.ext.SplitQName(name)
                if prefix:
                    self.__currElement.attrs["xmlns:"+prefix] = namespace
                else:
                    self.__currElement.attrs["xmlns"] = namespace
            for prefix in extraNss.keys():
                if prefix:
                    new_element.setAttributeNS(XMLNS_NAMESPACE,
                                               'xmlns:'+prefix,
                                               extraNss[prefix])
                else:
                    new_element.setAttributeNS(XMLNS_NAMESPACE,
                                               'xmlns',
                                               extraNss[''])
        return

    def endElement(self, name):
        self.__completeTextNode()
        if self.__currElement:
            self.__saxHandler.startElement(
                self.__currElement.name,
                self.__currElement.attrs
                )
            self.__currElement = None
        self.__saxHandler.endElement(name)
        return

    def text(self, text, escapeOutput=1):
        if self.__currElement:
            self.__saxHandler.startElement(
                self.__currElement.name,
                self.__currElement.attrs
                )
            self.__currElement = None
        self.__saxHandler.characters(text, 0, len(text))
        return

    def attribute(self, name, value, namespace=''):
        self.__currElement.attrs[name] = value
        if namespace:
            (prefix, local) = xml.dom.ext.SplitQName(name)
            if prefix:
                self.__currElement.attrs["xmlns:"+prefix] = namespace
        return

    def processingInstruction(self, target, data):
        self.__completeTextNode()
        if self.__saxHandler:
            self.__saxHandler.processingInstruction(target, data)
        else:
            self.__orphanedNodes.append(('pi', target, data))
        return

    def comment(self, body):
        self.__completeTextNode()
        if self.__saxHandler:
            self.__saxHandler.comment(body)
        else:
            self.__orphanedNodes.append(('comment', body))
        return

