########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Drivers/Util.py,v 1.8 2005/04/01 11:01:28 cogbuji Exp $
"""
Driver utilities

Copyright 2004 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

from Ft.Lib import Time
import FtssInputSource
from Ft.Xml.Xslt import StylesheetReader, XSL_NAMESPACE
from Ft.Xml.Xslt import Processor
from Ft.Server import FTSERVER_NAMESPACE, RESERVED_NAMESPACE
from Ft.Xml import Domlette, InputSource, XPath
from Ft.Rdf import Util as RdfUtil
import PathImp
from Ft.Server.Common import AclConstants, ResourceTypes
from Ft.Server.Common import Schema, XmlLib, Util
from Ft.Server.Server import FtServerServerException, Error
import cStringIO
from Ft.Xml.Domlette import Print
from Ft.Xml.XLink import XLINK_NAMESPACE
from Ft.Xml.XPath import Conversions

def CurrentTime():
    return str(Time.FromPythonTime())

class FtssDriverUtility:
    def generateUserDocDefStmts(self, ddPath, srcPath, justbuild=0):
        ddUri = Util.RepoPathToUri(ddPath)
        srcUri = Util.RepoPathToUri(srcPath)
        if self._getType(ddPath) == ResourceTypes.ResourceType.XPATH_DOCUMENT_DEFINITION:
            return self.generateUserXPathDocDefStmts(
                ddPath, ddUri, srcUri, srcPath, justbuild)
        else:
            return self.generateUserXsltDocDefStmts(
                ddPath, ddUri, srcUri, srcPath, justbuild)

    def generateUserXPathDocDefStmts(self, ddPath, ddUri, srcUri, srcPath, justbuild):
        ddPath = PathImp.QuickCreate(ddPath)
        con = self.getContext(ddPath)
        from Ft.Server.Common import DocumentDefinitionSerialization
        nsMap = DocumentDefinitionSerialization._DeserializeNsMaps(con)
        rdfMaps = DocumentDefinitionSerialization._DeserializeRdfMaps(con)
        baseNames = map(lambda x: x.object,
                        self._driver.getSystemModel().complete(
                            ddPath.absolutePath, Schema.BASE_DOCDEF, None)
                        )
        exprs = []
        for s, p, o, t in rdfMaps:
            exprs.append((XPath.Compile(s),
                          XPath.Compile(p),
                          XPath.Compile(o), t))

        con = self.getContext(srcPath, nsMap)

        results = []
        if justbuild:
            results.extend(
                self._applyExpressions(
                    srcPath, con, exprs,None,
                    scope=srcUri
                ))
        else:
            self._applyExpressions(
                srcPath, con, exprs, self._driver.getSystemModel(),
                scope=srcUri,
                extravars={(None,'dd-uri') : ddUri}
                )

        for baseUri in baseNames:
            results.extend(
                self.generateUserDocDefStmts(baseUri, srcPath, justbuild)
                )
        return results

    def generateUserXsltDocDefStmts(self, ddPath, ddUri, srcUri, srcPath, justbuild):
        systemExtModules = self._properties['XsltExtensionModule']
        baseNames = map(lambda x: x.object,
                        self._driver.getSystemModel().complete(
                            ddUri, Schema.BASE_DOCDEF, None)
                        )

        r = StylesheetReader.StylesheetReader()
        st = r.fromInstant(self.fetchResourceCache(ddPath), ddUri)

        extModules = []

        params = {}
        params[(RESERVED_NAMESPACE, 'display-path')] = srcPath.displayPath
        params[(RESERVED_NAMESPACE, 'absolute-path')] = srcPath.absolutePath
        params[(RESERVED_NAMESPACE, 'docdef-uri')] = ddUri

        p = None
        try:
            p = Processor.Processor()

            p.registerExtensionModules(
                ['Ft.Server.Server.Xslt'] + extModules + systemExtModules
                )

            #Create repository instance for extensions that may need one
            #This is a bit of a hack around the fact that doc defs are
            #Executed at such low level
            from Ft.Server.Server.SCore import RepositoryImp
            root = PathImp.CreateInitialPath('/', self)
            repo = RepositoryImp.RepositoryImp(root, self, '')
            p._repository = repo

            p.appendStylesheetInstance(st)

            content = self._driver.fetchFile(srcPath.absolutePath,
                                             srcPath.resourceType)

            isrc = FtssInputSource.FtssInputSourceFactory.fromString(
                content, srcUri, self)

            #p.inputSourceFactory = FtssInputSource._FtssInputSourceFactory(
            #    self._driver)
            p.inputSourceFactory = FtssInputSource.FtssInputSourceFactory
            rt = p.run(isrc, ignorePis=1, topLevelParams=params)
        except:
            import traceback; traceback.print_exc()
            raise FtServerServerException(Error.USER_XSLT_DOCDEF_EXCEPTION)

        #So we got a result.  Cool, deserialize it and add it.
        model, db = RdfUtil.DeserializeFromString(
            rt, create=1, scope=srcUri
            )

        results = []
        stmts = model.statements()
        stmts = [ (unicode(s.subject), unicode(s.predicate),
                   unicode(s.object), unicode(s.uri),
                   unicode(srcUri), unicode(s.objectType)) for s in stmts  ]

        if justbuild:
            results.extend(stmts)
        else:
            self._driver.getSystemModel()._driver.add(stmts)

        baseNames = map(lambda x:x.object,
                        self._driver.getSystemModel().complete(
                            ddUri, Schema.BASE_DOCDEF, None
                            ))

        for baseUri in baseNames:
            results.extend(
                self.generateUserDocDefStmts(baseUri, srcPath, justbuild)
                )
        return results

    def _applyXUpdate(self, path, xu):
        con = self.getContext(path)
        isrc = FtssInputSource.FtssInputSourceFactory.fromString(
            xu, path.absolutePathAsUri, self
        )
        xu = self._xupdateReader.fromSrc(isrc)
        self._xuProcessor.execute(con.node, xu)

        st = cStringIO.StringIO()
        Print(con.node, stream=st)
        res = st.getvalue()

        self._clearDomCache(path)
        return res,con.node

    def _applyCompiledXUpdate(self, path, xu, vars=None):
        con = self.getContext(path)
        self._xuProcessor.execute(con.node, xu,variables=vars)

        #This node has been changed its DOM cache gets Magically updated

        st = cStringIO.StringIO()
        Print(con.node, stream=st)
        res = st.getvalue()

        #self._clearDomCache(path)
        return res

    def _applyExpressions(self, path, context, exprs, model, scope,extravars=None):
        extravars = extravars or {}
        stmts = []
        context.varBindings[(None,'uri')] = path.absolutePath
        context.varBindings.update(extravars)
        for s, p, o, t in exprs:
            subjects = s.evaluate(context)
            if type(subjects) not in [type(()),type([])]:
                subjects = [subjects]
            predicates = p.evaluate(context)
            if type(predicates) not in [type(()),type([])]:
                predicates = [predicates]
            objects = o.evaluate(context)
            if type(objects) not in [type(()),type([])]:
                objects = [objects]
            for ns in subjects:
                for np in predicates:
                    for no in objects:
                        stmts.append((unicode(Conversions.StringValue(ns)),
                                      unicode(Conversions.StringValue(np)),
                                      unicode(Conversions.StringValue(no)),
                                      unicode(''),
                                      unicode(scope),
                                      unicode(t)))

        if model is not None:
            model._driver.add(stmts)
        return stmts

    def aclToXml(self,acl):
        fragments = ["<ftss:Acl>"]
        for key in acl.keys():
            for name,allowed in acl[key].items():
                fragments.append('  <ftss:Access type="%s" ident="%s" allowed="%d"/>' % (key, name, allowed==AclConstants.ALLOWED))
        fragments.append("</ftss:Acl>")
        return ''.join(fragments)

