########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Drivers/ResourceManager.py,v 1.7 2005/04/02 06:26:22 cogbuji Exp $
"""
Repo resource 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
import cPickle, FtssInputSource
from Ft.Server.Common import AclConstants, ResourceTypes
from Constants import UPDATE_ACL
from Constants import UPDATE_LAST_MODIFIED_AND_SIZE
from Util import CurrentTime
from Ft.Server.Common.Schema import g_resourceTypeFromRdf, g_rdfResourceTypes
from Ft.Xml import XPath

class ResourceManager:
    def createResource(self, path, metadata, content):
        """
        Create a new resource.  This function:
            - modifies the parent container
            - creates the metadata / content for the resource            
            - set's up the system metadata (via associated system doc defs)
        """
        self._verifyTx()

        if self._driver.hasFile(path.absolutePath, ResourceTypes.RESOURCE_METADATA):
            raise FtServerServerException(Error.PATH_EXISTS,
                                          path=path.displayPath)

        self._acl.verifyCreate(path)

        # Add this child to the parent container
        self._modifyParentContainer(path.getParentPath(), addPath=path.name)

        # Create the resource metadata first to give underlying datastore the oppurtunity to
        #determine the nature of the content (from resourceType) before it is stored
        metadata_path = path.normalize('.;metadata;no-traverse')
        self._driver.createFile(metadata_path.absolutePath,
                                ResourceTypes.RESOURCE_METADATA,
                                metadata)

        #Cache the Metadata DOM upfront (retrieved by setMetaData soon after)
        isrc = FtssInputSource.FtssInputSourceFactory.fromString(metadata,metadata_path.absolutePathAsUri, driver=self)
        mdDOM = FtssInputSource.NonvalidatingReader.parse(isrc)
        self._setDomCache(metadata_path, mdDOM)
        
        # Create the resource content
        self._driver.createFile(path.absolutePath,
                                 ResourceTypes.RESOURCE_CONTENT,
                                 content)

        #If the content is XML, cache content DOM upfront as well (for same reason)
        mdContext = XPath.Context.Context(mdDOM,processorNss={'ftss':'http://xmlns.4suite.org/reserved'})
        typRes = XPath.Compile('string(/ftss:MetaData/@type)').evaluate(mdContext)
        typRes = g_resourceTypeFromRdf[typRes]
        if typRes in ResourceTypes.XML_DOCUMENTS:
            isrc = FtssInputSource.FtssInputSourceFactory.fromString(content,path.absolutePathAsUri, driver=self)
            dom = FtssInputSource.NonvalidatingReader.parse(isrc)
            self._setDomCache(path, dom)
        self.setMetaData(path)
        return
    
    def setResourceCache(self, path, cache):
        """Pickle and store the cache"""
        if self._driver.hasFile(path.absolutePath,
                                ResourceTypes.RESOURCE_CACHE):
            self._driver.updateFile(path.absolutePath,
                                    ResourceTypes.RESOURCE_CACHE,
                                    cPickle.dumps(cache))
        else:
            self._driver.createFile(path.absolutePath,
                                    ResourceTypes.RESOURCE_CACHE,
                                    cPickle.dumps(cache))
        return

    def fetchResourceCache(self, path):
        """Un Pickle and return the cache"""
        if hasattr(path, 'displayPath'):
            path = path.absolutePath
        if self._driver.hasFile(path, ResourceTypes.RESOURCE_CACHE):
            content = self._driver.fetchFile(path, ResourceTypes.RESOURCE_CACHE)
            return cPickle.loads(content)
        raise FtServerServerException(Error.UNKNOWN_PATH, path=path)

    def hasResource(self, path):
        self._verifyTx()
##        try:
##            self.__acl.verifyFetch(path)
##        except FtServerServerException:
##            raise
##            return 0
        if self._hasCache(path): return 1
        return self._driver.hasFile(path.absolutePath, path.resourceType)

    def fetchResource(self, path):
        """Fetch the content (caching the results)"""
        self._verifyTx()

        if not self._tempFileDelete:
            self._acl.verifyFetch(path)

        #See if it is cached
        if self._hasCache(path):
            return self._getCache(path)
        if not path.resourceType == ResourceTypes.RESOURCE_METADATA and self.getType(path) in ResourceTypes.CONTAINERS:        
            content = self._driver.childReferenceXML(path)
        else:
            content = self._driver.fetchFile(path.absolutePath, path.resourceType)

        if content is None:
            raise FtServerServerException(Error.UNKNOWN_PATH,
                                          path=path.displayPath)
        self._setCache(path,content)
        return content

    def xupdateContent(self, path, xu):
        """
        Apply XUpdate to the content and store it (updatng the size and last modified system metadata)
        """
        self._verifyTx()
        self._acl.verifyWrite(path)       

        newContent,newContentDom = self._applyXUpdate(path, xu)

        #update Last modified time stamp and size
        mdPath = path.normalize('.;metadata;no-traverse')

        newMd = self._applyCompiledXUpdate(
            mdPath,
            UPDATE_LAST_MODIFIED_AND_SIZE,
            {(None,'lmd') : CurrentTime(),
             (None,'size') : str(len(newContent))
            }
        )


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

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

        #Update the system metadata
        self.updateMetaData(path)

    def updateResourceContent(self, path, content):
        """
        Update the specified resource with the given content (updating the
        last modified time and size)
        """
        self._verifyTx()
        self._acl.verifyWrite(path)

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

        newMd = self._applyCompiledXUpdate(mdPath,
                                             UPDATE_LAST_MODIFIED_AND_SIZE,
                                             {(None,'lmd') : CurrentTime(),
                                              (None,'size') : str(len(content))
                                              }
                                             )

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

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

    def deleteResource(self, path):
        """
        Delete the resource (clear its cache, delete all of its data-store
        components, remove from its parent, and delete its system metadata)
        """
        self._verifyTx()

        if path == '/':
            raise FtServerServerException(Error.PERMISSION_DENIED,
                                          level = 'delete',
                                          path = '/')

        if not self._tempFileDelete:
            self._acl.verifyDelete(path)

        # Remove the content and metadata from the cache
        mdPath = path.normalize('.;metadata;no-traverse')
        self._clearCache(path)
        self._clearCache(mdPath)
        self._clearDomCache(path)
        self._clearDomCache(mdPath)

        # Remove it from the repository
        self._driver.deleteFile(path.absolutePath,
                                ResourceTypes.RESOURCE_METADATA)
        if self.getType(path) in ResourceTypes.CONTAINERS:        
            self._driver.deleteContainer(path.absolutePath)
        else:
            self._driver.deleteFile(path.absolutePath,
                                    ResourceTypes.RESOURCE_CONTENT)
        if self._driver.hasFile(path.absolutePath,ResourceTypes.RESOURCE_CACHE):
            self._driver.deleteFile(path.absolutePath,ResourceTypes.RESOURCE_CACHE)

        # Remove from the parent container
        self._modifyParentContainer(path.getParentPath(), removePath=path.name)

        # Remove the statements for this from the model
        self.deleteMetaData(path.absolutePath)
        return

    def verifyDeleteResource(self,path):
        """Delete the resource"""
        self._verifyTx()
        if not self._tempFileDelete:
            self._acl.verifyDelete(path)
