########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Drivers/MetadataManager.py,v 1.4 2005/04/01 11:01:28 cogbuji Exp $
"""
Low level metadata operations for the driver

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

from Ft.Server.Server import FtServerServerException, Error
from Ft.Server import FTSERVER_NAMESPACE, RESERVED_NAMESPACE
from Ft.Lib import Time, Uuid
from Ft.Server.Common import AclConstants, ResourceTypes
from Constants import UPDATE_LAST_MODIFIED, UPDATE_LAST_MODIFIED_AND_SIZE
from Ft.Server.Common import Schema, XmlLib, Util
from Util import CurrentTime

class MetadataManager:
    def xupdateMetaData(self, path, xu):
        """
        Apply XUpdate to the metadata associated with the specified resource and store it (updating the last modified
        time stamp and size)
        """

        self._verifyTx()
        self._acl.verifyWrite(path)

        mdPath = path.normalize('.;metadata;no-traverse')

        #Dom cache is cleared by applyXUpdate!
        newMd,newMdDom = self._applyXUpdate(mdPath, xu)
        
        #Update with new MD Dom - for forthcoming call to applyXUpdate
        self._setDomCache(mdPath,newMdDom)

        #Update the last modified system metadata with the current time
        xu = XmlLib.MakeString(UPDATE_LAST_MODIFIED%(str(FTSERVER_NAMESPACE),
                                                       CurrentTime(),
                                                       ))

        newMd,newMdCache = self._applyXUpdate(mdPath,xu)

        self._driver.updateFile(mdPath.absolutePath,
                                ResourceTypes.RESOURCE_METADATA,
                                newMd)

        self._clearCache(mdPath)
        self._clearDomCache(mdPath)

        #Update the system metadata
        self.updateMetaData(path)
        return


    def updateResourceMetadata(self, path, newMd, changeDocDef=0):
        """Apply Update the md and store it (also updating the last modified and size system metadata)"""
        self._verifyTx()
        self._acl.verifyWrite(path)

        mdPath = path.normalize('.;metadata;no-traverse')

        contentPath = path.normalize('.;content;no-traverse')
        content = self.fetchResource(contentPath)

        #FIXME: Redundant?
        self._driver.updateFile(path.absolutePath,
                                ResourceTypes.RESOURCE_CONTENT, content)
        
        #Update metadata
        self._driver.updateFile(path.absolutePath,
                                ResourceTypes.RESOURCE_METADATA, newMd)

        self._clearCache(mdPath)
        self._clearDomCache(mdPath)

        #Might be redundant if the newMD already reflects the new time/size
        newMd = self._applyCompiledXUpdate(mdPath,
                                             UPDATE_LAST_MODIFIED_AND_SIZE,
                                             {(None,'lmd') : CurrentTime(),
                                              (None,'size') : str(len(content))
                                              }
                                             )

        self._driver.updateFile(path.absolutePath,
                                ResourceTypes.RESOURCE_METADATA, newMd)

        #Regenerate the system metadata (deleting it first if the user doc def changes)
        if changeDocDef:
            #If there is a change to the doc def, we need to do this the slow way
            #so that the second stage of generation uses the correct doc def
            self.deleteMetaData(path.absolutePath)
            self.setMetaData(path)
        else:
            self.updateMetaData(path)

        return

    def updateMetaData(self, path):
        newStmts = self.setMetaData(path, 1)
        oldStmts = []
        model = self._driver.getSystemModel()
        oldStmts.extend(model._driver.complete(
            path.absolutePath, None, None, None, Schema.SYSTEM_SOURCE_URI, {})
                        )
        oldStmts.extend(
            model._driver.complete(
                None, None, None, None,
                Util.RepoPathToUri(path.absolutePath), {})
                )

        toAdd = [ s for s in newStmts if s not in oldStmts ]
        toDel = [ s for s in oldStmts if s not in newStmts ]
        model._driver.add(toAdd)
        model._driver.remove(toDel)
        return

    def setMetaData(self, path, justBuild=0):
        mPath = path.normalize('.;metadata;no-traverse')
        con = self.getContext(mPath)

        res = self._typeExpression.evaluate(con)
        if not res:
            raise FtServerServerException(Error.INVALID_XML,message='No Type Attribute')
        rt = Schema.g_resourceTypeFromRdf[res[0].value]

        #First Set System MetaData
        exprs = self._systemDocDefs[rt][ResourceTypes.RESOURCE_METADATA]
        results = []
        if justBuild:
            results.extend(
                self._applyExpressions(path, con, exprs, None,scope=Schema.SYSTEM_SOURCE_URI)
                )
        else:
            self._applyExpressions(path, con, exprs,
                                    self._driver.getSystemModel(),
                                    scope=Schema.SYSTEM_SOURCE_URI)

        if rt not in (ResourceTypes.ResourceType.RAW_FILE,
                      ResourceTypes.ResourceType.URI_REFERENCE_FILE):
            con = self.getContext(path)

            exprs = self._systemDocDefs[rt][ResourceTypes.RESOURCE_CONTENT]
            if justBuild:
                results.extend(self._applyExpressions(path, con, exprs,None,
                                                       scope = Schema.SYSTEM_SOURCE_URI))
            else:
                self._applyExpressions(path, con, exprs,
                                        self._driver.getSystemModel(),
                                        scope = Schema.SYSTEM_SOURCE_URI)

            dd = self._driver.getSystemModel()._driver.complete(
                path.absolutePath, Schema.DOCDEF, None, None, None, {}
                )
            if dd and dd[0][2] != Schema.NULL_DOCDEF:
                #Update the user docdefs
                results.extend(
                    self.generateUserDocDefStmts(dd[0][2], path, justBuild)
                    )
        return results

    def deleteMetaData(self, path):
        model = self._driver.getSystemModel()
        model.removePattern(path, None, None, scope=Schema.SYSTEM_SOURCE_URI)
        model.removePattern(None, None, None, scope=Util.RepoPathToUri(path))
        return
