########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/SCore/ResourceMetaDataImp.py,v 1.56 2005/03/28 11:29:12 mbrown Exp $
"""
ResourceMetaData repository resource class.

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

import cStringIO, warnings

from Ft.Rdf import Statement
from Ft.Rdf import OBJECT_TYPE_LITERAL, OBJECT_TYPE_RESOURCE
from Ft.Server import RESERVED_NAMESPACE, FTSERVER_NAMESPACE
from Ft.Server.Common import ResourceTypes, Schema, AclConstants, XmlLib
from Ft.Server.Server import FtServerServerException, Error
from Ft.Server.Server.Drivers import FtssInputSource
import Ft.Server.Server.Xslt
from Ft.Xml import XUpdate, InputSource
from Ft.Xml.Domlette import Print
from Ft.Xml.XLink import XLINK_NAMESPACE
from Ft.Xml.XPath import Context, RuntimeException, CompiletimeException
import Ft.Xml.Xslt.Processor


class ResourceMetaDataImp:
    """
    Every repository resource inherits from this class in order to
    store and provide access to information about its existence.
    """

    resourceType = ResourceTypes.ResourceType.META_DATA

    def __init__(self, path, driver, cache):
        """
        Construct a new resource metadata object.
        This is almost always done through _fetchResource().

        Params:
         path is a reference to a PathImp that is the location of
           this resource in the in the repository.
         driver is an instance of FtssDriver that represents the
           connection to the actual on disk repository for use by
           the resource.
         cache is a reference to an ObjectCache instance that is a
           per-transaction cache of objects fetched within this
           transaction.
        """
        self._path = path
        self._deleted = 0
        self._driver = driver
        self._cache = cache
        self._cache.registerResource(self._path, self)
        self._basePath = self.getBasePath()

    ###############################################
    #General interfaces for getting information about the object
    ###############################################
    def getPath(self):
        """
        Get the path of this resource, as a Path instance.
        """
        self._verifyTx()
        return self._path

    def getBasePath(self):
        """
        Get the base path of this object, as a Path instance.
        The only difference between this and getPath is that for all
        non-container objects it will return the parent's path.
        For a container object it will return its path.
        """
        self._verifyTx
        return self._path.getParentPath()

    def getDisplayPath(self):
        """
        Get the path relative to the document root, as a string.
        """
        self._verifyTx()
        return self._path.displayPath

    def getAbsolutePath(self):
        """
        Get the full path into the system, as a string.
        """
        self._verifyTx()
        return self._path.absolutePath

    def getAbsolutePathAsUri(self):
        """
        Get the full path into the system, as an absolute URI string.
        """
        self._verifyTx()
        return self._path.absolutePathAsUri

    def getName(self):
        """
        Get the name of the resource, as a string.
        """
        return self._path.name

    def getCreationDate(self):
        """
        Get the date that this resource was created.
        The date will be an ISO 8601 string like u'2002-08-16T22:49:56Z'.
        """
        self._verifyTx()
        res = self._driver.getSystemModel().complete(self._path.absolutePath, Schema.CREATION_DATE, None)
        if not res:
            raise FtServerServerException(Error.UNKNOWN_PATH,
                                          path=self._path)
        return res[0].object

    def getLastModifiedDate(self):
        """
        Get the date that this resource was last modified.
        The date will be an ISO 8601 string like u'2002-08-16T22:49:56Z'.
        """
        self._verifyTx()
        res = self._driver.getSystemModel().complete(self._path.absolutePath, Schema.MODIFIED_DATE, None)
        if not res:
            raise FtServerServerException(Error.UNKNOWN_PATH,
                                          path=self._path)
        return res[0].object

    def getSize(self):
        """
        Get the size of the content, in bytes, as an integer.
        """
        self._verifyTx()

        res = self._driver.getSystemModel().complete(self._path.absolutePath, Schema.CONTENT_SIZE, None)
        if not res:
            raise FtServerServerException(Error.UNKNOWN_PATH,
                                          path=self._path)
        return int(res[0].object)

    def getResourceType(self):
        """
        Get the type of this resource.
        Types are defined in Ft.Server.Common.ResourceTypes.ResourceType.
        """
        self._verifyTx()
        return self.resourceType

    def isResourceType(self, testType):
        """
        Determine if this resource is of a certain type
        """
        self._verifyTx()
        return ResourceTypes._IsResourceType(self.getResourceType(), testType)

    def getContent(self):
        """
        Get the string content of this resource
        """
        self._verifyTx()
        return self._driver.fetchResource(self._path)

    def getContentSlice(self, start=None, end=None):
        """
        Get a piece of our content.  Really only makes sense for large raw files
        """
        if start:
            if end:
                return self.getContent()[start:end]
            return self.getContent()[start:]
        elif end:
            return self.getContent()[:end]
        return self.getContent()

    def getMetaDataResource(self):
        """
        Get the string meta data of this resource
        """
        self._verifyTx()
        return self

    def getContentResource(self):
        """
        Get the resource that represents the content of this object
        """
        self._verifyTx()
        p = self._path.normalize(".;content;no-traverse")
        return self._fetchResource(p)


    def getImt(self):
        """
        Returns the Internet Media Type of the raw file resource
        """
        self._verifyTx()
        return 'text/xml'

    def getValidationInfo(self):
        """
        Get the validation information associated with this resource.
        """
        self._verifyTx()
        return None


    def getParent(self):
        """
        Get the parent container of this resource
        """
        self._verifyTx()

        parentPath = self._path.getParentPath()
        return self._fetchResource(parentPath)

    def getRoot(self):
        """
        Get a reference to the system repository
        """
        self._verifyTx()
        return self._cache.getResource(self._path._documentRoot)

    def getAliases(self):
        """
        Get a list of alias objects that reference this object
        """
        self._verifyTx()
        aliases = []
        for st in self._driver.getSystemModel().complete(None,
                                                         Schema.ALIAS_REFERENCE,
                                                         self.getAbsolutePath()):

            aliases.append(self.fetchResource(st.subject+';no-traverse'))
        return aliases

    def getExtFunctionsAndElements(self):
        """
        All resource get to use the builtin functions.
        XsltDocuments override this to load the information from
        any associated document definitions.
        """
        return Ft.Server.Server.Xslt.ExtFunctions.copy(), Ft.Server.Server.Xslt.ExtElements.copy()

    #####################################
    #ACL Interfaces
    #####################################
    def addAcl(self, aclKey, aclIdent, allowed=1):
        """
        Add an acl identifier to the acl key.
        If the specified key does not exist on the resource
        this function has the same functionality of setAcl.
        """
        self._verifyTx()
        self._verifyAclIdent(aclIdent)
        acl = self._driver.getAcl(self._path)
        if aclKey not in acl:
            acl[aclKey] = {}
        if allowed:
            acl[aclKey][aclIdent] = AclConstants.ALLOWED
        else:
            acl[aclKey][aclIdent] = AclConstants.DENIED
        self._driver.setAcl(self._path,acl)
        return

    def getAcl(self, aclKey=None):
        """
        Get a list of users and groups specified by the aclKey.
        If an aclKey is specified, then the results will be a
        list of dictionaries keyed by acl identifiers and with
        values of ALLOWED or DISALLOWED (from AclConstants).  The
        values in the list of dictionaries could be inherited from
        the parent object. If no aclKey is specified, then the
        results will be a nested dictionary.  The outer dictionary
        is keyed by all of the defined acl keys on this resource.
        Each value of the outer dictionary will be a dictionary
        identical to when this method is called with a aclKey.
        These results will not return any inherited values.
        """
        self._verifyTx()
        return self._driver.getAcl(self._path, aclKey)

    def getAclIdentifiers(self):
        """
        Get a list of all acl identifiers for the current users.
        This is equivalent to performing an "expandGroups" of the
        current user. It will also add things such as "WORLD-GROUP"
        or "USER-GROUP" if they exist.
        """
        self._verifyTx()
        return self._driver.getAclIdentifiers()

    def getAclIdent(self):
        """
        Get the ACL identifier for the current user (without expansion).
        """
        self._verifyTx()
        return self._driver.getAclIdent()

    def inheritAcl(self, aclKey):
        """
        Remove the entire key from the ACL entry so that it is
        inherited again from its parent
        """
        self._verifyTx()
        acl = self._driver.getAcl(self._path)
        if aclKey not in acl:
            return
        del acl[aclKey]
        self._driver.setAcl(self._path,acl)
        return

    def removeAcl(self, aclKey, aclIdent):
        """
        Remove the acl identifier from the acl key.
        If the specified aclKey is not present on the resource or the
        specified aclIdent is not in the key, then do nothing.
        Remember that entries inherited from the parent are *not*
        included in the acl on the actual resurce.  In order to
        override inherited permissions, set to no access rather than
        trying to remove.
        """
        self._verifyTx()
        self._verifyAclIdent(aclIdent)
        acl = self._driver.getAcl(self._path)
        if aclKey not in acl:
            return
        if aclIdent not in acl[aclKey]:
            return
        del acl[aclKey][aclIdent]
        self._driver.setAcl(self._path,acl)
        return

    def setAcl(self, aclKey, aclIdent, allowed=1):
        """
        Replace the aclKey with a single entry of aclIdent.
        """
        self._verifyTx()
        self._verifyAclIdent(aclIdent)
        acl = self._driver.getAcl(self._path)
        if allowed:
            acl[aclKey] = {aclIdent:AclConstants.ALLOWED}
        else:
            acl[aclKey] = {aclIdent:AclConstants.DENIED}
        self._driver.setAcl(self._path,acl)
        return

    def verifyAcl(self, aclKey, verifyTraverse=1):
        """
        Test to see whether the current user (and the current users set
        of acl identifiers) is allowed to perform the specified action,
        aclKey, on the current object.  If verifyTraverse is specified,
        then execute premissions are tested on all of the parents of
        this resource as well.
        """
        self._verifyTx()
        return self._driver.verifyAcl(self._path, aclKey, verifyTraverse)

    def _verifyAclIdent(self, aclIdent):
        """
        This is a helper function to determine is an aclIdentifer is valid.
        """
        if aclIdent in [AclConstants.WORLD_GROUP_NAME,
                        AclConstants.USERS_GROUP_NAME,
                        AclConstants.SUPER_USER_GROUP_NAME,
            AclConstants.ANONYMOUS_USER_NAME,
                        AclConstants.OWNER]:
            return 1
        if aclIdent in self.getRoot().getAllUserNames():
            return 1
        if aclIdent in self.getRoot().getAllGroupNames():
            return 1
        raise FtServerServerException(Error.INVALID_ACL_IDENT,
                                      aclIdent=aclIdent)

    def getOwner(self):
        """
        Get the owner of this resource.
        """
        self._verifyTx()
        ownerName = self._driver.getSystemModel().complete(self.getAbsolutePath(),
                                                           Schema.OWNER,
                                                           None)[0].object
        return self.getRoot().fetchUserOrGroupByName(ownerName)

    def setOwner(self, newOwner):
        """
        Change the owner of this resource.
        """
        self._verifyTx()
        self._driver.verifyAcl(self.getPath(), AclConstants.CHANGE_OWNER_ACCESS, 0)
        xu = SET_OWNER_XUPDATE%(str(FTSERVER_NAMESPACE), str(XLINK_NAMESPACE),
                                newOwner.getName())
        xu = XmlLib.MakeString(xu)
        self._driver.xupdateMetaData(self._path, xu)
        return

    ##########################################
    #XML Interfaces
    ##########################################

    def applyXslt(self, chain, params=None, ignorePis=1,
                  extParams=None, extModules=None):
        """
        applies the specified chain of stylesheets (with the specified
        transformation parameters) using the "self" object as source
        chain - can be a list of transforms, in which case only one
                transform run will execute, with the list of transforms
                all used such that [a.xsl, b.xsl] is equivalent to
                adding an import of b.xsl to a.xsl and applying a.xsl
                can also be a list of lists of transforms, in which case
                a chain of transforms is run,
                one pass for each inner list.  The output of each
                step in the chain is fed as the input to the next
        params - a dictionary giving the overridden top-level parameters
                for the transforms
        ignorePis - whether or not to ignore <xml-stylesheet processing
                instructions in the source document
        extParams - a special dictionary of parameters that allows the
                processor to communicate special values back to the caller
        extModules - a list of modules containing extension function and
                element implementations
        """
        extModules = extModules or []

        params = params or {}
        params[(RESERVED_NAMESPACE, 'display-path')] = self._path.displayPath
        params[(RESERVED_NAMESPACE, 'absolute-path')] = self._path.absolutePath
        params[(RESERVED_NAMESPACE, 'absolute-path-uri')] = self._path.absolutePathAsUri
        if self._path.absolutePath == '/':
            params[(RESERVED_NAMESPACE, 'base-path')] = self._basePath.absolutePath
        else:
            params[(RESERVED_NAMESPACE, 'base-path')] = self._basePath.absolutePath + '/'
        extParams = extParams or {}
        extParams[(RESERVED_NAMESPACE, 'path')] = self._path.absolutePath

        #First map the stylesheets
        if not isinstance(chain, (list, tuple)):
            chain = (chain,)
        if not isinstance(chain[0], (list, tuple)):
            chain = (chain,)

        #prep a base URI for figuring out the URI of the result
        # of applying each stylesheet in the chain
        base_uri = self._path.absolutePathAsUri
        if self.resourceType == ResourceTypes.ResourceType.CONTAINER and \
            base_uri[-1] != '/':
            base_uri += '/'

        rt = None
        for stylesheets in chain:
            p, rt, imt = self._xsltExecute(None, stylesheets, params,
                                      extParams, extModules, ignorePis)
            c = 0
            while p.chainTo:
                #Chained-to stylesheets must be repo paths for now
                next = self.fetchResource(p.chainTo)
                #Generate URI of previous result, which is now the
                # source document.
                c += 1
                prevResultUri = FtssInputSource.FTSS_RESOLVER.normalize(
                    'chain-result-' + str(c) + '.xml', base_uri)
                if p.chainParams: params.update(p.chainParams)
                p, rt, imt = self._xsltExecute(
                    rt, [next], params, extParams, extModules,
                    ignorePis, srcUri=prevResultUri)
        return rt, imt

    def asDom(self, stripElements=None):
        """
        Return the content of this object as a non-live DOM instance.
        This means that any mutations to the returned DOM object will
        not affect the content of this resource. An exception will be
        raised if the content cannot be parsed as XML.
        """
        self._verifyTx()
        return FtssInputSource.NonvalidatingReader.parseString(
            self.getContent(), self._path.absolutePathAsUri, self._driver,
            stripElements = stripElements)

    def toXPathContext(self,nss=None):
        """
        Create an XPath Context with the src of this RawFile.
        nss is a dictionary of namespace definitions to include in the
        context.
        """
        self._verifyTx()
        return self._driver.getContext(self._path,nss)


    def xUpdate(self, updateSrc):
        """
        Allows XML content to be updated with the XUpdate protocol.
        updateSrc is a string that represents the XUpdate to be applied
        to the resource. extraNss is a dict of additional namespace
        bindings to pass to the XUpdate processor, if necessary.
        """
        xureader = XUpdate.Reader()

        isrc = FtssInputSource.FtssInputSourceFactory.fromString(
            updateSrc, self._path.absolutePath, self._driver
        )
        xu = xureader.fromSrc(isrc)
        processor = XUpdate.Processor()
        dom = self.asDom()
        processor.execute(dom, xu)

        st = cStringIO.StringIO()
        Print(dom, stream=st)
        self.setContent(st.getvalue())
        return

    #####################################
    #Query Interfaces
    #####################################
    def hasResource(self, path):
        """
        Query if the system has a resource specified by the path.
        No exception will be raised by this function.  If the current
        user cannot access the resource or the resource does not exist
        in the repository, then the results will be 0.  If the resource
        does exist, then the results will be the integer resource type
        of the resource (see
        Ft.Server.Common.ResourceTypes.ResourceTypes).
        """
        self._verifyTx()
        path = self._basePath.normalize(path)
        if self._driver.hasResource(path):
            return self._driver.getType(path)
        return 0

    #####################################
    #Retrieval Interfaces
    #####################################
    def fetchResource(self, path, traverseAliases=None):
        """
        Fetch a resource from the system.
        path is a string.  The traverseAliases parameter is deprecated
        and the new ";traverse" path arguments should be used.  As an
        example, where before you could call:
        resource.fetchResource('path',1) now you would call
        resource.fetchResource('path;traverse')
        """
        self._verifyTx()
        if (traverseAliases != None):
            warnings.warn("You are using a deprecated traverse alias feature.  Please specify with path arguments ;traverse or ;no-traverse",
                  DeprecationWarning, 2)
            if traverseAliases:
                path += ";traverse"
            else:
                path += ";no-traverse"
        path = self._basePath.normalize(path)
        return self._fetchResource(path)


    ##########################################
    #Retrieval Interfaces
    ##########################################

    def _fetchResource(self, path):
        """
        Internal call to fetch a resource.
        path is an instance of PathImp.
        """

        if path.displayPath == '/' and path.resourceType != ResourceTypes.RESOURCE_METADATA:
            return self.getRoot()

        obj = self._cache.getResource(path)
        if obj is not None:
            return obj

        self._driver.verifyAcl(path, AclConstants.READ_ACCESS, 1)

        if path.resourceType == ResourceTypes.RESOURCE_METADATA:
            #Get the MetaData object
            return ResourceMetaDataImp(path, self._driver, self._cache)

        rt = self._driver.getType(path)

        if rt == ResourceTypes.ResourceType.RAW_FILE:
            import RawFileImp
            obj = RawFileImp.RawFileImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.URI_REFERENCE_FILE:
            import UriReferenceFileImp
            obj = UriReferenceFileImp.UriReferenceFileImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.XML_DOCUMENT:
            import XmlDocumentImp
            obj = XmlDocumentImp.XmlDocumentImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.COMMAND:
            import CommandImp
            obj = CommandImp.CommandImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.SERVER:
            import ServerImp
            obj = ServerImp.ServerImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.XSLT_DOCUMENT:
            import XsltDocumentImp
            obj = XsltDocumentImp.XsltDocumentImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.RDF_DOCUMENT:
            import RdfDocumentImp
            obj = RdfDocumentImp.RdfDocumentImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.CONTAINER:
            import ContainerImp
            obj = ContainerImp.ContainerImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.GROUP:
            import GroupImp
            obj = GroupImp.GroupImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.USER:
            import UserImp
            obj = UserImp.UserImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.XPATH_DOCUMENT_DEFINITION:
            import XPathDocumentDefinitionImp
            obj = XPathDocumentDefinitionImp.XPathDocumentDefinitionImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.XSLT_DOCUMENT_DEFINITION:
            import XsltDocumentDefinitionImp
            obj = XsltDocumentDefinitionImp.XsltDocumentDefinitionImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.ALIAS:
            import AliasImp
            obj = AliasImp.AliasImp(path, self._driver, self._cache)
        elif rt == ResourceTypes.ResourceType.SCHEMATRON_DOCUMENT:
            import SchematronDocumentImp
            obj = SchematronDocumentImp.SchematronDocumentImp(path, self._driver, self._cache)
        else:
            raise FtServerServerException(Error.UNKNOWN_RESOURCE_TYPE, type=rt)
        return obj


    ##########################################
    #Mutation Interfaces
    ##########################################
    def setContent(self,src):
        """
        Set the string content of this resource.
        """
        self._verifyTx()
        self._driver.updateResourceContent(self._path, src)
        return

    def delete(self):
        """
        Delete this object from the repository.
        """
        self._verifyTx()
        self._driver.verifyDeleteResource(self._path)
        self._delete()

        self._cache.unregisterResource(self._path)

        #Just incase
        mdPath = self._path.normalize('.;metadata;no-traverse')
        self._cache.unregisterResource(mdPath)
        conPath = self._path.normalize('.;content;no-traverse')
        self._cache.unregisterResource(conPath)

        self._deleted = 1
        return

    ###################################
    #Creation Interfaces
    ###################################
    def addAlias(self,path,docDef = None):
        """
        Adds an alias for this resource to the repository.
        path is the string path of the new alias.
        docDef is the document definition to associate with the new alias.
        """
        self._verifyTx()
        path = self._basePath.normalize(path)
        if docDef:
            docDefPath = self._basePath.normalize(docDef)
            if self._driver.getType(docDefPath) not in ResourceTypes.DOCUMENT_DEFINITIONS:
                raise FtServerServerException(Error.INVALID_PATH,
                                              path=docDefPath.displayPath,
                                              type='Document Definition')
        else:
            docDefPath = None

        import AliasImp
        md, content = AliasImp.NewAliasXml(self._driver,
                                          self._path.absolutePath,
                                          path.name,
                                          {},
                                          self._driver.getAclIdent(),
                                          docDefPath and docDefPath.absolutePath or None)

        self._driver.createResource(path, md, content)
        return self._fetchResource(path)

    ######################################
    #Delete Interface
    ######################################
    def removeAlias(self,path):
        """
        Removes the specified alias for this resource.
        """
        self._verifyTx()
        path = self._basePath.normalize(path + ';no-traverse')
        res = self._fetchResource(path)
        res.delete()
        return

    ########################################
    #RDF Interfaces
    ########################################

    def getModel(self):
        """
        Retrieves the common model associated w/ the repository
        """
        self._verifyTx()
        return self._driver.getModel(self._path)

    def addLiteralProperty(self, predicate, object, scope='', statementUri=''):
        """
        Adds a statement about this repository object, with a literal property, to
        the model
        """
        stmt = Statement.Statement(self.getAbsolutePath(),
                                   predicate,
                                   object,
                                   scope = scope,
                                   statementUri=statementUri,
                                   objectType=OBJECT_TYPE_LITERAL)
        self._driver.getModel(self._path).add([stmt])
        return stmt

    def addResourceProperty(self, predicate, object, scope='', statementUri=''):
        """
        Adds a statement about this repository object, with a resource
        property, to the model
        """
        stmt = Statement.Statement(self.getAbsolutePath(),
                                   predicate,
                                   object.getUri(),
                                   scope=scope,
                                   statementUri=statementUri,
                                   objectType=OBJECT_TYPE_RESOURCE)
        self._driver.getModel(self._path).add([stmt])
        return stmt

    def removeResourceProperty(self, predicate, object):
        """
        Remove a statement about this repository object, with a resource preoperty, from
        the model
        """
        self._driver.getModel(self._path).removePattern(self._path.absolutePath, predicate,object.getUri())
        return

    def removeLiteralProperty(self, predicate, object):
        """
        Removes a statement about this repository object, with a literal property, from
        the model
        """
        self._driver.getModel(self._path).removePattern(self._path.absolutePath,predicate,object)

    def getStatementsAbout(self):
        """
        Returns all statements about this object in the model
        """
        return self._driver.getModel(self._path).complete(self._path, None, None)

    def getStatementsGeneratedBy(self):
        """
        Returns all statements in the model generated by this object
        """
        return self._driver.getModel(self._path).complete(None, None, None, scope=self._path)

    ########################################
    #Temporary file Interfaces
    ########################################
    def markTemporary(self, timeToLive):
        """
        Mark this resource as temporary.
        Temporary objects will be removed from the repository when
        their timeToLive has expired and purgeTemporaryResources() is
        called on the repository. Time to live is seconds from now.
        purgeTemporaryResources() is most often called from the
        controller and is based on the controller configuration option
        TemporaryReapInterval.
        """
        self.getRoot().setPathTemporary(self._path.absolutePath, timeToLive)
        return

    ##########################################
    #String interfaces
    ##########################################
    def __str__(self):
        return "<%s at %x path='%s'>" % (ResourceTypes.g_resourceTypeMap[self.getResourceType()],
                                         id(self),
                                         self._path.absolutePath)


    ######################################
    #Internal Interfaces
    ######################################

    def _verifyTx(self):
        self._driver._verifyTx()
        if self._deleted:
            raise FtServerServerException(Error.OBJECT_DELETED, path=self._path)

    def _delete(self):
        self._driver.deleteResource(self._path)
        return

    def _xsltExecute(self, src, stylesheets, params, extParams, extModules,
                     ignorePis, srcUri=''):
        """
        Applies an XSLT stylesheet to this object, or to the given src
        string (if any). If src is given, srcUri should also be given,
        to establish the URI of the src. srcUri is treated as a URI
        reference, resolved against this object's URI. If multiple
        stylesheets are given, they are chained (each is applied to the
        result of the previous transformation).
        """

        p = Ft.Xml.Xslt.Processor.Processor()
        p.extensionParams = extParams

        # Register our general extension modules
        p.registerExtensionModules(['Ft.Server.Server.Xslt']+extModules)

        p._repository = self.getRoot()

        p.setDocumentReader(FtssInputSource.NonvalidatingReader)

        for s in stylesheets:
            #print "stylesheet %r" % s
            if isinstance(s, (str, unicode)):
                #print "creating FtssInputSource from URI %r" % self._path.absolutePathAsUri
                isrc = FtssInputSource.FtssInputSourceFactory.fromUri(
                    self._path.absolutePathAsUri, self._driver)
                #print "using it to get a new FtssInputSource for system ID %r" % s
                isrc = isrc.resolve(s, '', 'REPO APPLYXSLT')
                #print "new FtssInputSource has uri %r" % isrc.uri
                p.appendStylesheet(isrc)
            elif hasattr(s, 'toStylesheet'):
                #A document reference
                p.appendStylesheetInstance(s.toStylesheet(self))
            elif hasattr(s, 'asStylesheet'):
                #A reference to a document in the system
                p.appendStylesheetInstance(s.asStylesheet())

                #See if it has any ext functions
                f, e = s.getExtFunctionsAndElements()
                p.extFunctions.update(f)
                p.extElements.update(e)

        p.inputSourceFactory = FtssInputSource._FtssInputSourceFactory(
            self._driver)

        if src:
            uri = FtssInputSource.FTSS_RESOLVER.normalize(srcUri,
                                           self._path.absolutePathAsUri)
            isrc = p.inputSourceFactory.fromString(src, uri)
            rt = p.run(isrc, ignorePis=ignorePis, topLevelParams=params)
        else:
            uri = self._path.absolutePathAsUri
            if self._path.resourceType == ResourceTypes.RESOURCE_METADATA:
                uri += ';metadata'
            uri += ';no-traverse'
            #print "!!! creating FtssInputSource from URI %r" % uri
            isrc = p.inputSourceFactory.fromUri(uri, self._driver)
            rt = p.runNode(self.asDom(), uri, ignorePis=ignorePis,
                           topLevelParams=params, docInputSource=isrc)

        if extParams is not None and hasattr(p, 'extensionParams'):
            extParams.update(p.extensionParams)

        imt = p._lastOutputParams.mediaType
        return p, rt, imt


SET_OWNER_XUPDATE="""<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:update select="/ftss:MetaData/ftss:Owner">%s</xupdate:update>
</xupdate:modifications>"""


UPDATE_TIME_TO_LIVE_XUPDATE="""<?xml version="1.0"?>
<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:update select="/ftss:MetaData/ftss:TimeToLive">%s</xupdate:update>
</xupdate:modifications>
"""


SET_TIME_TO_LIVE_XUPDATE="""<?xml version="1.0"?>
<xupdate:modifications
  version="1.0"
  xmlns:xupdate="http://www.xmldb.org/xupdate"
  xmlns:ftss="%s"
  xmlns:xlink="%s"
>
  <xupdate:append select="/ftss:MetaData"><ftss:TimeToLive>%s</ftss:TimeToLive></xupdate:append>
</xupdate:modifications>
"""
