########################################################################
#
# File Name:            CopyOfElement.py
#
# Documentation:        http://docs.4suite.org/4XSLT/CopyOfElement.py.html
#
"""
Implementation of the XSLT Spec copy-of element.
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
"""


from xml.xslt import StylesheetReader, Stylesheet
import cStringIO, string
import os, urllib

from xml.xslt import XSL_NAMESPACE
from Ft.Lib import pDomlette

def CompileString(xslt, outFileName=None):
    st = cStringIO.StringIO(xslt)
    CompileStream(st, outFileName)

def CompileUri(uri,baseUri='',outFileName = None):
    if os.path.exists(uri):
        stream = open(uri)
    else:
        stream = urllib.urlopen(uri)
    CompileStream(stream,outFileName,baseUri=baseUri)

def CompileStream(stream,outFileName = None,baseUri = ''):

    r = StylesheetReader.StylesheetReader()

    sty = r.fromStream(stream,baseUri=baseUri)

    file,ext = os.path.splitext(outFileName)
    if ext != '.py':
        outFileName = outFileName + '.py'
    
    c = StylesheetCompilier()
    c.compile(sty,outFileName)


class StylesheetCompilier:
    def __init__(self):
        pass

    def compile(self, sty,outFileName):
        outFile = open(outFileName,'w')
        self._templates = {}
        self._selectPatterns = []
        self._primeFile(sty,outFile)
        self._imports = []
        self._compileNode(outFile,sty,1)
        self._inRtf = 0
        outFile.write("\n\n\n")
        matchPatterns,namedPatterns = self._writeTemplates(outFile,1)
        #self._writePatterns(outFile)
        self._postFile(outFile,matchPatterns,namedPatterns)

    def _primeFile(self,sty,outFile):
        outFile.write(FILE_HEADER % (repr(sty.outputParams.__dict__),))

    def _postFile(self,outFile,patterns,nPatterns):
        d = "{"
        for mode,pi in patterns.items():
            d = d + repr(mode) + ': ['
            for p in pi:
                d = d + '(%s,%s,%s,%s,%s,%s),' % (repr(p[0]),repr(p[1]),repr(p[2]),p[3],p[4],p[5])
            d = d + '],'
                
        d = d + '}'

        d2 = "{"
        for name,func in nPatterns.items():
            d2 = d2 + "%s:%s," % (repr(name),func)
        d2 = d2 + '}'

        #d3 = "{"
        #for e in self._selectPatterns:
        #    d3 = d3 + '%d:XPATH_PARSER.parse("""%s"""),' % (id(e),repr(e))
        #d3 = d3 + '}'

        l1 = "["
        for name in self._imports:
            l1 = l1 + "%s," % repr(name)
        l1 = l1 + "]"
        outFile.write(POST_FILE % (d,d2,l1))

    def _compileNode(self,outFile,node,indent = 0):

        if node.nodeType == pDomlette.Node.TEXT_NODE:
            self._compileText(outFile,node,indent)
        elif node.namespaceURI == XSL_NAMESPACE:
            print node.localName
            if node.localName == 'stylesheet':
                self._compileStylesheetNode(outFile,node,indent)
            elif node.localName == 'output':
                pass
            elif node.localName == 'param':
                self._compileParam(outFile,node,indent)
            elif node.localName == 'template':
                self._compileTemplate(outFile,node,indent)
            elif node.localName == 'apply-templates':
                self._compileApplyTemplates(outFile,node,indent)
            elif node.localName == 'text':
                self._compileLiteralText(outFile,node,indent)
            elif node.localName == 'import':
                self._imports.append(node.href)
            elif node.localName == 'variable':
                self._compileVariable(outFile,node,indent)
            elif node.localName == 'comment':
                self._compileComment(outFile,node,indent)
            else:
                raise "Not SUPPORTED %s" % node.localName
        else:
            self._compileLiteralElement(outFile,node,indent)

    def _compileMatch(self,outFile,match,indent):
        name = match.__class__.__name__
        if name == 'ParsedNameNodeTest':
            raise Foo
            self._writeIndent(outFile,indent)
            outFile.write("if self.node.nodeType != g_domModule.Node.ELEMENT_NODE:\n")
            self._writeIndent(outFile,indent+1)
            outFile.write("return 0\n")
            self._writeIndent(outFile,indent)
            outFile.write("if '%s' != self.node.localName:\n" % match.localPart)
            self._writeIndent(outFile,indent+1)
            outFile.write("return 0\n")
            self._writeIndent(outFile,indent)
            if match.prefix:
                outFile.write("try:\n")
                self._writeIndent(outFile,indent+1)
                outFile.write("return self.processorNss['%s'] == self.node.namespaceURI\n" % match.prefix)
                self._writeIndent(outFile,indent)
                outFile.write("except:\n")
                self._writeIndent(outFile,indent+1)
                outFile.write("raise Exception('Unknown prefix %s')" % self.prefix)
            else:
                outFile.write("return self.node.namespaceURI == ''\n")

        elif name == 'RootPathPattern':
            return "lambda s: s.node.ownerDocument == s.node"
        elif name == 'RootPattern':
            return "lambda s:s.node.ownerDocument == s.node"
        elif name == 'ParsedTextNodeTest':
            return "lambda s:s.node.nodeType == %d"%pDomlette.Node.TEXT_NODE
        elif name == 'ParsedWildcardNodeTest':
            return "lambda s:s.node.nodeType == %d"%pDomlette.Node.ELEMENT_NODE
        elif name == 'NodeNameTest':
            return "lambda s:s.node.nodeName == '%s'" % match._nodeName
        else:
            raise "Not Supported %s %s %s" % (match,match.__class__,name)

    def _compileChildren(self,outFile,node,indent = 0):
        for child in node.childNodes:
            self._compileNode(outFile,child,indent + 1)

    def _compileStylesheetNode(self,outFile,node,indent):
        self._writeIndent(outFile,indent)
        outFile.write("def main(self,node,writer,topLevelParams):\n")

        self._writeIndent(outFile,indent+1)
        outFile.write("topLevelParams = topLevelParams or {}\n")

        self._writeIndent(outFile,indent+1)
        outFile.write("#Prime the stylesheet\n")

        #Apply Templates
        self._writeIndent(outFile,indent+1)
        outFile.write("self.node = node\n")        
        self._compileChildren(outFile,node,indent)

        self._writeIndent(outFile,indent+1)
        outFile.write("self.applyTemplates(writer,None)\n")



    def _compileTemplate(self,outFile,node,indent):
        if node._name != ('',''):
            self._templates[node] = node.getAttributeNS('','name')
        else:
            self._templates[node] = str(id(node))


    def _compileText(self,outFile,node,indent):
        self._writeIndent(outFile,indent)
        outFile.write("writer.text('''%s''')\n" % node.data)


    def _compileLiteralText(self,outFile,node,indent):
        self._writeIndent(outFile,indent)
        value = node.firstChild and node.firstChild.data or ''
        if node._disable_output_escaping:
            outFile.write("writer.text('''%s''',escapeOutput=0)\n" % value)
        else:
            outFile.write("writer.text('''%s''')\n" % value)

    def _compileLiteralElement(self,outFile,node,indent):
        self._writeIndent(outFile,indent)
        outFile.write("writer.startElement('%s', '%s', %s)\n" % (node.nodeName,
                                                               node.namespaceURI,
                                                               repr(node._outputNss),
                                                               ))
        self._compileChildren(outFile,node,indent-1)
        self._writeIndent(outFile,indent)
        outFile.write("writer.endElement('%s')\n" % (node.nodeName,
                                                     ))
        
    def _compileApplyTemplates(self,outFile,node,indent):

        self._writeIndent(outFile,indent,"origState = self.__dict__.copy()\n")
        self._writeIndent(outFile,indent,'self.processorNss = """%s"""\n' % repr(node._nss))

        if node._expr:
            self._selectPatterns.append(node._expr)
            self._writePattern(outFile,node._expr,indent)
        else:
            self._writeIndent(outFile,indent)
            outFile.write("node_set = self.node.childNodes\n")

        self._writeIndent(outFile,indent,"pos = 1\n")
        self._writeIndent(outFile,indent,"size = len(node_set)\n")
        self._writeIndent(outFile,indent,"for n in node_set:\n")
        self._writeIndent(outFile,indent+1,"self.node, self.position, self.size = (n,pos,size)\n")
        self._writeIndent(outFile,indent+1,"self.applyTemplates(writer,None)\n")
        self._writeIndent(outFile,indent+1,"pos = pos + 1\n")

        self._writeIndent(outFile,indent,"self.__dict__ = origState\n")


    def _compileVariable(self,outFile,node,indent):

        if node._select:
            if node._expr.__class__.__name__ == 'ParsedNLiteralExpr':
                self._writeIndent(outFile,indent,"self.varBindings[%s] = %s\n" % (repr(node._name),node._select))
            else:
                raise "Unsupport select %s" % node._expr.__class__.__name__
        else:
            outFile.write("\n")
            self._writeIndent(outFile,indent)
            outFile.write("#Determine Variable %s\n\n" % str(node._name))
            self._writeIndent(outFile,indent)
            outFile.write("oldWriter = writer\n")
            self._writeIndent(outFile,indent)
            outFile.write("writer = RtfWriter.RtfWriter(self._outputParams,self._dummyDoc)\n")
            self._inRtf = 1
            self._compileChildren(outFile,node,indent-1)
            self._inRtf = 0
            self._writeIndent(outFile,indent)
            outFile.write("self.varBindings[%s] = writer.getResult()\n"%repr(node._name))
            self._writeIndent(outFile,indent)
            outFile.write("writer = oldWriter\n")

    _compileParam = _compileVariable
                
    def _compileComment(self,outFile,node,indent):
        self._writeIndent(outFile,indent)
        outFile.write("origState = self.__dict__.copy()\n")
        self._writeIndent(outFile,indent)
        outFile.write("self.processorNss = '''%s'''\n" % repr(node._nss))
        if not self._inRtf:
            self._writeIndent(outFile,indent)
            outFile.write("oldWriter = writer\n")
            self._writeIndent(outFile,indent)
            outFile.write("writer = RtfWriter.RtfWriter(self._outputParams,self._dummyDoc)\n")
        self._compileChildren(outFile,node,indent-1)
        self._writeIndent(outFile,indent)
        outFile.write("oldWriter.comment(Conversions.StringValue(writer.getResult()))\n")
        if not self._inRtf:
            self._writeIndent(outFile,indent)
            outFile.write("writer = oldWriter\n")
        self._writeIndent(outFile,indent)
        outFile.write("self.__dict__ = origState\n")
    def _writeTemplates(self,outFile,indent):
        patternDict = {}
        namedDict = {}
        for t,name in self._templates.items():
            safeName = string.replace(name,"-","_")
            aName = 'template_%s_apply'%safeName
            mName = 'template_%s_match'%safeName

            patternInfo,mode,nss = t.getMatchInfo()

            self._writeIndent(outFile,indent)
            outFile.write("def %s(self,writer):\n" % (aName))
            self._compileChildren(outFile,t,indent)


            if t._name != ('',''):
                if not namedDict.has_key(t._name):
                    namedDict[t_name] = aName

            patterns = []

            for pi in patternInfo:
                patterns.append((pi[2],mode,nss,pi[0]))

            patterns.sort(lambda x, y: cmp(y[0], x[0]))

            ctr = 0
            for p in patterns:
                inline = self._compileMatch(outFile,p[-1],indent+1)
                m = p[1]
                if not patternDict.has_key(m):
                    patternDict[m] = []
                if not inline:
                    self._writeIndent(outFile,indent)
                    outFile.write("def %s_%d(self):\n" % (mName,ctr))
                    outFile.write(outline)
                    newP = (p[0],p[1],p[2],'%s_%d' % (mName,ctr),'',aName)
                else:
                    newP = (p[0],p[1],p[2],'None',inline,aName)
                patternDict[m].append(newP)

                ctr = ctr + 1

        return patternDict, namedDict


    def _writePattern(self,outFile,expr,indent):
        if expr.__class__.__name__ == 'ParsedStep':
            self._writeAxis(outFile,expr._axis,expr._nodeTest,indent)
            if expr._predicates:
                raise "Not Supported"
        else:
            raise "Unknown Pattern %s" % node._expr.__class__.__name__

    def _writeAxis(self,outFile,axis,nodeTest,indent):
        if axis.__class__.__name__ == 'ParsedChildAxisSpecifier':
            self._writeNodeTest(outFile,nodeTest,indent)
            
            self._writeIndent(outFile,indent)
            outFile.write("node_set = filter(lambda node, test=nodeTest,s=self:test(s,node),self.node.childNodes)\n")
            self._writeIndent(outFile,indent)
            outFile.write("reverse=0\n")
        else:
            raise "Unknown supported axis %s" % axis.__class__.__name__

    def _writeNodeTest(self,outFile,nodeTest,indent):
        if nodeTest.__class__.__name__ == 'NodeNameTest':
            self._writeIndent(outFile,indent)
            outFile.write("nodeTest = lambda s,n:n.nodeName == '%s'\n" % nodeTest._nodeName)
        else:
            raise "Unknown Node Test %s" % nodeTest.__class__.__name__ 

    def _writeIndent(self,outFile,indent,msg=''):
        outFile.write(("    "*indent) + msg)


FILE_HEADER="""#!/usr/bin/env python
#
#Generated file, do not edit
#

import xml.xslt
import xml.xpath
from xml.xslt import OutputParameters
from xml.xslt import TextWriter

import os
BETA_DOMLETTE = os.environ.get("BETA_DOMLETTE")
if BETA_DOMLETTE:
    from Ft.Lib import cDomlette
    g_readerClass = cDomlette.RawExpatReader
    g_domModule = cDomlette
else:
    from Ft.Lib import pDomlette
    g_readerClass = pDomlette.PyExpatReader
    g_domModule = pDomlette




class Stylesheet:
    OUTPUT_PARAMS_DICT = %s
    OUTPUT_PARAMS = OutputParameters()
    OUTPUT_PARAMS.__dict__.update(OUTPUT_PARAMS_DICT)

    def __init__(self,
                 node=None,
                 position=1,
                 size=1,
                 currentNode=None,
                 varBindings=None,
                 processorNss=None,
                 stylesheet=None,
                 mode=None):

        self.node = node
        self.position = position
        self.size = size
        self.varBindings = varBindings or {}
        self.processorNss = processorNss or {}
        self._cachedNss = None
        self._cachedNssNode = None
        self.currentNode = currentNode
        self.stylesheet = stylesheet
        self.mode = mode
        self.currentInstruction = None
        self.documents = {}
        self.rtfs = []

    def executeString(self,xmlString,writer=None, topLevelParams=None, reader = None):
        reader = reader or g_readerClass()
        #try:
        if 1:
            import cStringIO
            stream = cStringIO.StringIO(xmlString)
            src = reader.fromStream(stream)

        return self.executeNode(src,writer,topLevelParams)

    def executeStream(self,stream,writer=None, topLevelParams=None, reader = None):
        reader = reader or g_readerClass()
        #try:
        if 1:
            src = reader.fromStream(stream)

        return self.xecuteNode(src,writer,topLevelParams)


    def executeNode(self,node,writer=None,topLevelParams = None):
        if writer == None:
            writer = TextWriter.TextWriter(self.OUTPUT_PARAMS)
        self.main(node,writer,topLevelParams)
        return writer.getResult()


    #################
    #The code from the stylesheet
    #################
    
"""

POST_FILE="""
    MATCHED_PATTERNS = %s
    NAMED_PATTERNS = %s
    IMPORTS = %s
    def applyTemplates(self,writer,mode):

        matched = 0
        for patternInfo in self.MATCHED_PATTERNS.get(mode,[]):
            if apply(patternInfo[4] or patternInfo[3],(self,)):
                matched = 1
                apply(patternInfo[5],(self,writer))

        if not matched:
            if self.node.nodeType == g_domModule.Node.TEXT_NODE:
                writer.text(self.node.data)
            elif self.node.nodeType in [g_domModule.Node.ELEMENT_NODE, g_domModule.Node.DOCUMENT_NODE]:
                origState = self.__dict__.copy()
                size = len(self.node.childNodes)
                pos = 1
                for n in self.node.childNodes:
                    self.node, self.position, self.size = (n,pos,size)
                    self.applyTemplates(writer,None)
                    pos = pos + 1
                self.__dict__ = origState
            elif self.node.nodeType == g_domModule.Node.ATTRIBUTE_NODE:
                writer.text(self.node.value)

if __name__ == '__main__':
    import sys
    s = Stylesheet(None)
    print s.executeStream(sys.stdin)
"""
