########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/SCore/DocumentDefinitionImp.py,v 1.18 2003/03/22 17:38:30 uogbuji Exp $
"""
DocumentDefinition repository resource class.

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

import cStringIO
from types import ListType

import XmlDocumentImp

from Ft.Rdf import Statement
from Ft.Server import FTSERVER_NAMESPACE
from Ft.Server.Common import ResourceTypes, Schema
from Ft.Server.Common import DocumentDefinitionSerialization
from Ft.Server.Common import CreationParams, XmlLib
from Ft.Server.Server import FtServerServerException, Error
from Ft.Server.Server.Drivers import FtssDriver
from Ft.Xml import Domlette, XPath
from Ft.Xml.Domlette import Print
from Ft.Xml.XPath import Conversions, Context
from Ft.Xml.XLink import XLINK_NAMESPACE


class DocumentDefinitionImp(XmlDocumentImp.XmlDocumentImp):
    """
    A document definition document
    """
    resourceType = ResourceTypes.ResourceType.DOCUMENT_DEFINITION

    def addBaseDocumentDefinition(self, baseDef):
        """
        Add a base document definition to this document definition.
        This will cause all of the documents associated with this
        document definition to be re-processed and could take a long time
        """
        self._verifyTx()
        if not baseDef.isResourceType(ResourceTypes.ResourceType.DOCUMENT_DEFINITION):
            raise FtServerServerException(Error.INVALID_PATH,
                                          path = baseDef.getPath().displayPath,
                                          type='Document Definition')

        xu = XmlLib.MakeString(NEW_BASE_XUPDATE % (FTSERVER_NAMESPACE, XLINK_NAMESPACE, baseDef.getPath().absolutePath))
        self._driver.xupdateContent(self._path, xu)
        self._update()
        return

    def setContent(self, src):
        XmlDocumentImp.XmlDocumentImp.setContent(self, src)
        self._update()
        return

    def _delete(self):
        self._verifyTx()
        for pdd in self.getDerivedDefinitions():
            pdd.removeBaseDocumentDefinition(self)

        #Set all of our meta documents to have a None doc def
        for path in self.getMetaDocumentUris(recurse = 0):
            #Just do a reset of the content
            doc = self.fetchResource(path)
            doc.setDocumentDefinition(None)

        XmlDocumentImp.XmlDocumentImp._delete(self)
        return

    def getBaseDefinitions(self):
        """
        Get the base definitions of this document definition
        """
        self._verifyTx()
        paths = [ x.object for x in self._driver.getSystemModel().complete(self._path.absolutePath, Schema.BASE_DOCDEF, None) ]
        res = []
        for p in paths:
            path = self._basePath.normalize(p)
            res.append(self._fetchResource(path))
        return res

    def getCreationParams(self):
        """
        Get the creation parameters associated with this document definition.  See Ft.Server.Core.CreationParamsImp.py
        """
        self._verifyTx()
        context = self.toXPathContext()
        cp = CreationParams._Deserialize(context)
        return cp

    def getDerivedDefinitions(self):
        """
        Get all of the parent definitions of this document definition
        """
        paths = [ x.subject for x in self._driver.getSystemModel().complete(None, Schema.BASE_DOCDEF, self._path.absolutePath) ]
        res = []
        for p in paths:
            path = self._basePath.normalize(p)
            res.append(self._fetchResource(path))
        return res

    def getMetaDocumentUris(self, recurse=1):
        """
        Get a list of all documents that use this doc def
        """
        res = []
        if recurse:
            for bd in self.getBaseDefinitions():
                res.extend(bd.getMetaDocumentUris())
        res.extend([ x.subject for x in self._driver.getSystemModel().complete(None, Schema.DOCDEF, self.getPath().absolutePath) ])
        return res

    def removeBaseDocumentDefinition(self, baseDef, recurse=1):
        """
        Remove a base document definition.  This will cause all of the documents
        associated with this document definition to be regenerated.
        """
        if not baseDef.isResourceType(ResourceTypes.ResourceType.DOCUMENT_DEFINITION):
            raise FtServerServerException(Error.INVALID_PATH,
                                          path = baseDef.getPath().displayPath,
                                          type='Document Definition')

        self._verifyTx()
        xu = XmlLib.MakeString(REMOVE_BASE_XUPDATE%(FTSERVER_NAMESPACE, XLINK_NAMESPACE,
						    baseDef.getPath().absolutePath))
        self._driver.xupdateContent(self._path, xu)
        self._update()
        return

    def setCreationParams(self, newCp):
        """
        Set the creation parameters of this document definition
        """
        self._verifyTx()

        doc = Domlette.implementation.createDocument(None, None, None)
        cp = CreationParams._Serialize(doc, newCp)

        st = cStringIO.StringIO()
        Print(cp, stream=st)
        xu = XmlLib.MakeString(
            SET_CREATION_PARAMS_XUPDATE%(FTSERVER_NAMESPACE, XLINK_NAMESPACE,
                                         st.getvalue()))
        self._driver.xupdateContent(self._path, xu)
        self._update()
        return

    def _update(self):
        """This is called when ever we need to regenerate all of our meta documents"""
        #This process could take a long time
        #First Do our meta documents,
        #Then, do all of our derived definitions
        for path in self.getMetaDocumentUris(recurse = 0):
            #Just do a reset of the content
            doc = self.fetchResource(path)
            #FIXME: Isn't there a more efficient way to do this?
            doc.setContent(doc.getContent())
        for dd in self.getDerivedDefinitions():
            dd._update()
        return


def NewDocumentDefinitionXml(driver, path, acl, owner, imt, src, docDef, typ):
    a = driver.aclToXml(acl)
    t = FtssDriver.CurrentTime()
    md = """<ftss:MetaData xmlns:ftss="%s" path='%s' type='%s' creation-date='%s' document-definition='%s'>
  %s
  <ftss:LastModifiedDate>%s</ftss:LastModifiedDate>
  <ftss:Owner>%s</ftss:Owner>
  <ftss:Imt>%s</ftss:Imt>
  <ftss:Size>%d</ftss:Size>
</ftss:MetaData>
        """ % (FTSERVER_NAMESPACE, path, Schema.g_rdfResourceTypes[typ],
               t, docDef or Schema.NULL_DOCDEF, a, t, owner, imt, len(src),
               )
    return XmlLib.MakeString(md)


NEW_BASE_XUPDATE="""<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:append select="/*/ftss:BaseNames" child="last()">
    <ftss:Base xlink:type="simple" xlink:href="%s" xlink:actuate="onLoad" xlink:show="embed"/>
  </xupdate:append>
</xupdate:modifications>
"""

REMOVE_BASE_XUPDATE="""<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:remove select="/*/ftss:BaseNames/ftss:Base[@xlink:href='%s']"/>
</xupdate:modifications>
"""


SET_CREATION_PARAMS_XUPDATE="""<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:remove select="/*/ftss:CreationParams"/>
  <xupdate:append select="/*" child='last()'>
    %s
  </xupdate:append>
</xupdate:modifications>
"""
