########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Drivers/FtssDriver.py,v 1.71 2005/04/02 06:30:23 cogbuji Exp $
"""
4Suite repository driver

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

import time, cStringIO, cPickle
from Constants import UPDATE_LAST_MODIFIED, UPDATE_LAST_MODIFIED_AND_SIZE,NEW_CONTAINER_CHILD_XUPDATE,DELETE_CONTAINER_CHILD_XUPDATE,UPDATE_LAST_MODIFIED,UPDATE_SIZE,UPDATE_ACL

import pAclObject as AclObject
import FtssModel, FtssInputSource
import PathImp

from Ft.Lib import Time, Uuid
from Ft.Rdf import Statement, Model
from Ft.Rdf import OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_LITERAL, OBJECT_TYPE_RESOURCE
from Ft.Rdf.Drivers import Memory as RdfMemory
from Ft.Rdf.Serializers import Dom as RdfDom
import Ft.Rdf.Util
from Ft.Server import FTSERVER_NAMESPACE, RESERVED_NAMESPACE
from Ft.Server.Common import AclConstants, ResourceTypes
from Ft.Server.Common import Schema, XmlLib, Util
from Ft.Server.Server import FtServerServerException, Error
from Ft.Xml import Domlette, InputSource, XPath, XLink, XUpdate
from Ft.Xml.Domlette import Print
from Ft.Xml.XLink import XLINK_NAMESPACE
from Ft.Xml.XPath import Conversions
from Ft.Xml.Xslt import StylesheetReader, XSL_NAMESPACE
import Ft.Xml.Xslt.Processor

from ResourceManager import ResourceManager
from ACLManager import ACLManager
from MetadataManager import MetadataManager
from SessionManager import SessionManager
from Util import FtssDriverUtility,CurrentTime

DEFAULT_SESSION_TIME_TO_LIVE = 60*15 #15 mins

_CONTAINER_SKELETON = """<ftss:Container xmlns:ftss="%s" xmlns:xlink="%s">
  <ftss:Children>%%s</ftss:Children>
</ftss:Container>""" % (FTSERVER_NAMESPACE.encode('iso-8859-1'),
                        XLINK_NAMESPACE.encode('iso-8859-1'))

_CONTAINER_CHILD_FRAGMENT = """\
<ftss:ChildReference
    xlink:type='simple'
    xlink:actuate='onLoad'
    xlink:show='embed'
    xlink:href='%s;metadata'/>\n"""

def _GetContainerXml(paths):
    """
    paths is a sequence of unicode path names
    """
    fragments = [ _CONTAINER_CHILD_FRAGMENT % path.encode('iso-8859-1')
                  for path in paths ]
    return _CONTAINER_SKELETON % (''.join(fragments))


class DataStoreContainer:
    """
    Repository XML Container object.  createContainer/getContent/setContent/_getChildren are overidden
    to use low level api's to implement containment.  Should catch Exceptions more ferverently.

    NOTE
    self._driver is the FtssDriver instance
    self._driver._driver is the Data store driver instance

    """
    def _createContainer(self,path,content):
        """
        Overidden by underlying Data Store to do what it needs / wants to in order to represent a container.
        Those that don't will create a 'Resource' with the provided content (regardless of whether or not it
        is automatically generated later
        """
        self.createFile(path.absolutePath, ResourceTypes.RESOURCE_CONTENT,
                        content)

    def createContainer(self, path, createParents=0, docDef=None,
                        actualContent=None):
        """
        Creates the specified container, if createParents is true
        then all its parents along the path are created (if they
        dont exist). If actualContent is given NOT_SUPPORTED Exception is raised
        Needs to mimick (mostly) and override FtssDriver.createResource
        in order to call _createContainer
        """
        self._verifyTx()

        if not hasattr(path, 'displayPath'):
            path = self._basePath.normalize(path)

        parentPath = path.getParentPath()

        if not self._driver.hasResource(parentPath):
            if createParents:
                self.createContainer(parentPath.displayPath, createParents=1)
            else:
                raise FtServerServerException(
                    Error.INVALID_PATH, path=parentPath.absolutePath,
                    type='Container'
                    )
        if self._driver.getType(parentPath) not in ResourceTypes.CONTAINERS:
            raise FtServerServerException(Error.INVALID_PATH,
                                          path=parentPath,
                                          type='Container')

        if actualContent:
            self._driver.rollback()
            raise FtServerServerException(Error.NOT_SUPPORTED,
                                          reason='Cant provide XML content for datastore containers')

        md, content = self._driver.newContainerXml(
            path.name, {}, self._driver.getAclIdent(),
            docDef
            )

        if self._driver._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._driver._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._driver.createFile(metadata_path.absolutePath,
                                ResourceTypes.RESOURCE_METADATA,
                                md)

        # Create the resource content
        self._driver._driver._createContainer(path,content)
        self._driver.setMetaData(path)
        return self._fetchResource(path)

    def deleteContainer(self, path):
        self.deleteFile(path, ResourceTypes.RESOURCE_CONTENT)
        return

    def _getChildren(self):
        """
        Uses underlying datastore implementation
        """
        self._verifyTx()
        return self._driver._driver.fetchChildren(self.getPath())

    def childReferenceXML(self, path):
        """
        Returns an XML representation of the specified container (path is a
        PathImp)
        Uses fetchChildren (implemented by the data store) to retrieve the
        names of the children and generates a repository container XML
        document.
        """
        return _GetContainerXml(map(unicode, self.fetchChildren(path)))


class FtssDriver(ResourceManager, ACLManager, MetadataManager,
                 FtssDriverUtility, SessionManager):
    """
    The FtssDriver wraps the lowest-level repository database driver,
    providing functions commonly used by higher-level APIs.
    """

    def __init__(self, logger, driverMod, properties):

        self._acl = None
        self._aclIdent = None

        self._properties = properties

        self._driver = None
        self._driverMod = driverMod
        self._driverProperties = properties['Driver']

        self._cache = {}
        self._domCache = {}
        self._logger = logger

        self._xupdateReader = XUpdate.Reader()
        self._xuProcessor = XUpdate.Processor()

        self._tempFileDelete = 0  #Set when temp files are being removed
        return

    def exists(self):
        """A very special-case function.  This can be called without being logged in"""
        rt = self._driverMod.Exists(self._driverProperties)
        if rt == 1:
            #Make sure there is a user
            driver = self._driverMod.Begin(**self._driverProperties)
            rt = len(driver.getSystemModel().complete(
                None, Schema.TYPE,
                Schema.g_rdfResourceTypes[ResourceTypes.ResourceType.USER])) > 0
            driver.rollback()
            return rt
        return rt

    def initialize(self):
        """Another very special case that will create a new '/'.  It cannot be called if they are logged in"""
        if self._acl:
            raise FtServerServerException(Error.PERMISSION_DENIED)
        self._driverMod.Initialize(self._driverProperties)

        self._driver = apply(self._driverMod.Begin,(),self._driverProperties)

        acl = self.defaultAcl(AclConstants.SUPER_USER_GROUP_NAME,
                              AclConstants.SUPER_USER_GROUP_NAME,
                              )

        acl[AclConstants.READ_ACCESS][AclConstants.WORLD_GROUP_NAME] = AclConstants.ALLOWED
        acl[AclConstants.EXECUTE_ACCESS][AclConstants.WORLD_GROUP_NAME] = AclConstants.ALLOWED

        md, content = self.newContainerXml('/', acl,
                                           AclConstants.SUPER_USER_GROUP_NAME,
                                           None)

        self._driver.createFile('/', ResourceTypes.RESOURCE_METADATA,md)
        self._driver.createFile('/', ResourceTypes.RESOURCE_CONTENT,content)

        #Do a pseudo login
        self._acl = AclObject.AclObject(self, 0, [AclConstants.SUPER_USER_GROUP_NAME])
        self._aclIdent = AclConstants.SUPER_USER_GROUP_NAME

        p = PathImp.QuickCreate('/')
        self.setMetaData(p)
        return

    def destroy(self):
        """Another very special case that will destroy a '/'.
        It cannot be called if they are logged in"""
        if self._acl:
            raise FtServerServerException(Error.PERMISSION_DENIED)
        return self._driverMod.Destroy(self._driverProperties)

    _passwordExpression = XPath.Compile('/ftss:User/ftss:PasswdHash')

    def login(self, userName, password, verify=0):
        if verify and self.exists() != 1:
            raise FtServerServerException(Error.CONFIG_INVALID_REPOSITORY)

        self._driver = apply(self._driverMod.Begin, (), self._driverProperties)
        keep = 0
        try:
            (aclIdent, aclIdents, anon) = self.checkLogin(userName, password)
            keep = 1
        finally:
            if not keep:
                self._driver.rollback()
                self._driver = None

        self._aclIdent = aclIdent
        self._acl = AclObject.AclObject(self, anon, aclIdents)
        return

    def checkLogin(self, userName, password):
        if userName is None or userName == AclConstants.ANONYMOUS_USER_NAME:
            #Anon
            aclIdents = [AclConstants.WORLD_GROUP_NAME]
            aclIdent = AclConstants.WORLD_GROUP_NAME
            anon = 1
        else:
            model = self._driver.getSystemModel()
            stmts = model.complete(None, Schema.USER_NAME, userName)
            if len(stmts) != 1:
                raise FtServerServerException(Error.INVALID_LOGIN)

            userUri = stmts[0].subject

            #Make sure it is a user
            #userMetaData = self._driver.fetchFile(userUri, ResourceTypes.RESOURCE_METADATA)
            if self._getType(userUri) != ResourceTypes.ResourceType.USER:
                raise FtServerServerException(Error.INVALID_LOGIN)

            userPath = PathImp.QuickCreate(userUri)
            con = self.getContext(userPath)

            res = self._passwordExpression.evaluate(con)

            if not res:
                raise FtServerServerException(Error.INVALID_LOGIN)

            pw = Conversions.StringValue(res[0])

            aclIdent = userName
            if pw != password:
                raise FtServerServerException(Error.INVALID_LOGIN)

            aclIdents = [aclIdent,
                         AclConstants.WORLD_GROUP_NAME,
                         AclConstants.USERS_GROUP_NAME]
            aclIdents.extend(self._expandGroups(userUri))
            anon = 0
        #If we get this far, login is valid
        return (aclIdent, aclIdents, anon)

    def getContext(self, path, nss=None):
        defaultNss = {'ftss': FTSERVER_NAMESPACE,
                      'xlink': XLINK_NAMESPACE,
                      'xsl': XSL_NAMESPACE,
                      }
        if nss:
            defaultNss.update(nss)
        if self._hasDomCache(path):
            dom = self._getDomCache(path)
        else:
            #Return the generated content if a container.  Avoid metadata paths
            if not path.resourceType == ResourceTypes.RESOURCE_METADATA and self.getType(path) in ResourceTypes.CONTAINERS:
                content = str(self._driver.childReferenceXML(path))
            else:
                content = self._driver.fetchFile(path.absolutePath,
                                             path.resourceType)
            if content is None:
                raise FtServerServerException(Error.UNKNOWN_PATH,
                                              path=path.absolutePath)
            isrc = FtssInputSource.FtssInputSourceFactory.fromString(content,path.absolutePathAsUri, driver=self)
            dom = FtssInputSource.NonvalidatingReader.parse(isrc)
            self._setDomCache(path, dom)
        return XPath.Context.Context(dom, processorNss=defaultNss)

    def getModel(self,path):
        self._verifyTx()
        s =  self._driver.getSystemModel()
        u = self._driver.getUserModel()
        return FtssModel.FtssModel(s, u, self._acl, path)

    def getSystemModel(self):
        self._verifyTx()
        return self._driver.getSystemModel()

    def getSystemContainerPath(self):
        self._verifyTx()
        return self._systemContainer

    def commit(self):
        self._verifyTx()
        self._driver.commit()
        self._acl = self._driver = None
        return

    def rollback(self):
        self._verifyTx()
        self._driver.rollback()
        self._acl = self._driver = None

    def _modifyParentContainer(self, path, addPath='', removePath=''):
        """
        Update the parent container content and metadata.  Only one of
        addPath or removePath may be specified.

        path is the PathImp for the parent container
        addPath and removePath specify the child path to add or remove
        """
        if self.getType(path) not in ResourceTypes.CONTAINERS:
            raise FtServerServerException(Error.INVALID_PATH,
                                          path=path.displayPath,
                                          type='Container')

        if addPath and removePath:
            raise TypeError("Only one of addPath or removePath allowed")
        elif addPath:
            self._driver.manageChildren(path, addPath, True)
        elif removePath:
            self._driver.manageChildren(path, removePath, False)
        else:
            raise TypeError("One of addPath or removePath are required")

        content = self._driver.childReferenceXML(path)

        metadata_path = path.normalize('.;metadata;no-traverse')
        modified_date = CurrentTime()
        content_size = str(len(content))
        metadata = self._applyCompiledXUpdate(metadata_path,
                                                UPDATE_LAST_MODIFIED_AND_SIZE,
                                                {(None, 'lmd') : modified_date,
                                                 (None, 'size') : content_size,
                                                 })
        self._driver.updateFile(metadata_path.absolutePath,
                                metadata_path.resourceType,
                                metadata)

        # Uncache the parent
        self._clearCache(path)
        self._clearCache(metadata_path)
        self._clearDomCache(path)
        self._clearDomCache(metadata_path)

        # From the content we add a statement for the new child
        # From the metadata we change ftss:LastModified and ftss:Size

        model = self._driver.getSystemModel()

        # Delete the existing ftss:LastModified
        model.removePattern(path.absolutePath, Schema.MODIFIED_DATE, None,
                            scope=Schema.SYSTEM_SOURCE_URI)

        # Delete the existing ftss:Size
        model.removePattern(path.absolutePath, Schema.CONTENT_SIZE, None,
                            scope=Schema.SYSTEM_SOURCE_URI)

        # Add statements for ftss:LastModified, ftss:Size.
        statements = [Statement.Statement(path.absolutePath,
                                          Schema.MODIFIED_DATE,
                                          modified_date,
                                          scope=Schema.SYSTEM_SOURCE_URI,
                                          objectType=OBJECT_TYPE_LITERAL),
                      Statement.Statement(path.absolutePath,
                                          Schema.CONTENT_SIZE,
                                          content_size,
                                          scope=Schema.SYSTEM_SOURCE_URI,
                                          objectType=OBJECT_TYPE_LITERAL),
                      ]

        model.add(statements)

        #Handle document definition updates to the model
        docdefs = model.complete(path.absolutePath, Schema.DOCDEF, None)
        if docdefs and docdefs[0].object != Schema.NULL_DOCDEF:
            model.removePattern(None, None, None,
                                scope=Util.RepoPathToUri(path.absolutePath))
            self.generateUserDocDefStmts(docdefs[0].object, path)

        return

    def open(self,href):
        ###Bits of a stream interface
        #Turn it into a path
        p = PathImp.CreateInitialPath('/',
                                      self)
        p = p.normalize(href+';no-traverse') #Should we not traverse here?
        src = self.fetchResource(p)
        return cStringIO.StringIO(str(src))

    def setTempFileDelete(self, flag):
        self._tempFileDelete = flag
        return

    _typeExpression = XPath.Compile('//ftss:MetaData/@type')

    def getType(self,path):
        """Get the resource type from the metadata. path is a Path object"""

        res = self._driver.getSystemModel().complete(path.absolutePath, Schema.TYPE, None)

        if not res:
            #from pprint import pprint
            #from inspect import stack
            #pprint(stack())
            raise FtServerServerException(Error.UNKNOWN_PATH,path=path.displayPath)
            #NOTE: Should an error really be raised?
            #What of the scenario where a resource is being created for the first time
            #and has no associated metadata in the model yet?
            #return None

        return Schema.g_resourceTypeFromRdf[res[0].object]

    def _getType(self, path):
        """Get the resource type from the metadata. path is an absolute path string"""

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

    def _expandGroups(self,userUri):
        """Expand a user's groups"""
        model = self._driver.getSystemModel()
        res = model.complete(None,Schema.GROUP_MEMBER,userUri+';metadata')
        groupUris = map(lambda x:x.subject,res)
        groups = map(lambda x:x.split('/')[-1],groupUris)
        for g in groupUris:
            groups.extend(self._expandGroups(g))
        return groups

    def _verifyTx(self):
        if self._driver == None or self._acl == None:
            raise FtServerServerException(Error.TRANSACTION_NOT_IN_PROGRESS)

    def newContainerXml(self,path,acl,owner,docDef,content=None):
        rt = ResourceTypes.ResourceType.CONTAINER
        if content is None:
            content = _GetContainerXml([])

        a = self.aclToXml(acl)
        t = CurrentTime()
        docDef = docDef or Schema.NULL_DOCDEF
        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>text/xml</ftss:Imt>
<ftss:Size>%d</ftss:Size>
</ftss:MetaData>
        """ % (str(FTSERVER_NAMESPACE), path, Schema.g_rdfResourceTypes[rt],
        t, docDef, a, t, owner, len(content))

        return XmlLib.MakeString(md), XmlLib.MakeString(content)

    #Cache interfaces
    def _hasCache(self,path):
        return (path.absolutePath, path.resourceType) in self._cache
    def _setCache(self,path, content):
        self._cache[(path.absolutePath, path.resourceType)] = content
    def _getCache(self, path):
        return self._cache[(path.absolutePath, path.resourceType)]
    def _clearCache(self, path):
        k = (path.absolutePath, path.resourceType)
        if k in self._cache:
            del self._cache[k]


    def _hasDomCache(self,path):
        return (path.absolutePath, path.resourceType) in self._domCache
    def _setDomCache(self, path, content):
        self._domCache[(path.absolutePath, path.resourceType)] = content
    def _getDomCache(self, path):
        return self._domCache[(path.absolutePath, path.resourceType)]
    def _clearDomCache(self, path):
        k = (path.absolutePath, path.resourceType)
        if k in self._domCache:
            del self._domCache[k]

    systemDocDefs = {}

    for rt in ResourceTypes.RAW_FILES:
        systemDocDefs[rt] = {}
        systemDocDefs[rt][ResourceTypes.RESOURCE_METADATA] = []
        systemDocDefs[rt][ResourceTypes.RESOURCE_CONTENT] = []
    del rt

    cur = systemDocDefs[ResourceTypes.ResourceType.RAW_FILE][ResourceTypes.RESOURCE_METADATA]
    cur.extend([('$uri', '"%s"'%Schema.TYPE, '/ftss:MetaData/@type', OBJECT_TYPE_RESOURCE),
                ('$uri', '"%s"'%Schema.CREATION_DATE, '/ftss:MetaData/@creation-date', OBJECT_TYPE_LITERAL),
                ('$uri', '"%s"'%Schema.MODIFIED_DATE, '/ftss:MetaData/ftss:LastModifiedDate', OBJECT_TYPE_LITERAL),
                ('$uri', '"%s"'%Schema.IMT, '/ftss:MetaData/ftss:Imt', OBJECT_TYPE_LITERAL),
                ('$uri', '"%s"'%Schema.CONTENT_SIZE, '/ftss:MetaData/ftss:Size', OBJECT_TYPE_LITERAL),
                ('$uri', '"%s"'%Schema.OWNER, '/ftss:MetaData/ftss:Owner', OBJECT_TYPE_RESOURCE),
                ('$uri', '"%s"'%Schema.TIME_TO_LIVE, '/ftss:MetaData/ftss:TimeToLive', OBJECT_TYPE_LITERAL),
                ])
    del cur

    #URI Ref Documents
    cur = systemDocDefs[ResourceTypes.ResourceType.URI_REFERENCE_FILE][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.RAW_FILE][ResourceTypes.RESOURCE_METADATA][:]
    cur.append(('$uri', '"%s"'%Schema.URI_REFERENCE_LOCATION, "/ftss:MetaData/ftss:Reference", OBJECT_TYPE_RESOURCE))
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.URI_REFERENCE_FILE][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.RAW_FILE][ResourceTypes.RESOURCE_CONTENT][:]
    del cur


    #XML Documents
    cur = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.RAW_FILE][ResourceTypes.RESOURCE_METADATA][:]
    cur.append(('$uri', '"%s"'%Schema.DOCDEF, "/ftss:MetaData/@document-definition", OBJECT_TYPE_RESOURCE))
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT]= systemDocDefs[ResourceTypes.ResourceType.RAW_FILE][ResourceTypes.RESOURCE_CONTENT][:]
    del cur

    #CONTAINERS
    cur = systemDocDefs[ResourceTypes.ResourceType.CONTAINER][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    #Group
    cur = systemDocDefs[ResourceTypes.ResourceType.GROUP][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.GROUP][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.GROUP_MEMBER,"/ftss:*/ftss:Members/ftss:MemberReference/@xlink:href", OBJECT_TYPE_RESOURCE))
    cur.append(('$uri','"%s"'%Schema.GROUP_NAME,"/ftss:Group/@name", OBJECT_TYPE_LITERAL))
    del cur

    #User
    cur = systemDocDefs[ResourceTypes.ResourceType.USER][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.USER][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.USER_NAME,"/ftss:User/@userName", OBJECT_TYPE_LITERAL))
    del cur

    #Document Definition
    systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION] = {}
    systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA] = []
    systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT] = []

    cur = systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.FULL_TEXT_INDEX,"/*/ftss:CreationParams/@FullTextIndex", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.XSLT_EXT_MODULE,"/*/ftss:CreationParams/ftss:ExtensionModule/@PyImport", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.ENFORCE_RDF_SCHEMA,"/*/ftss:CreationParams/@EnforceSchema", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.BASE_DOCDEF,"/*/ftss:BaseNames/ftss:Base/@xlink:href", OBJECT_TYPE_RESOURCE))
    cur.append(('$uri','"%s"'%Schema.XML_VALIDATION_TYPE,"/*/ftss:CreationParams/ftss:Validator/@type", OBJECT_TYPE_RESOURCE))

    del cur


    #XPath Doc Def
    cur = systemDocDefs[ResourceTypes.ResourceType.XPATH_DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.XPATH_DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT][:]
    del cur

    #Xslt Doc Def
    cur = systemDocDefs[ResourceTypes.ResourceType.XSLT_DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.XSLT_DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.DOCUMENT_DEFINITION][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.STYLESHEET_DEPENDENCY,"//xsl:include/@href", OBJECT_TYPE_RESOURCE))
    cur.append(('$uri','"%s"'%Schema.STYLESHEET_DEPENDENCY,"//xsl:import/@href", OBJECT_TYPE_RESOURCE))
    del cur


    #Xslt Document
    cur = systemDocDefs[ResourceTypes.ResourceType.XSLT_DOCUMENT][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.XSLT_DOCUMENT][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.STYLESHEET_DEPENDENCY,"//xsl:include/@href", OBJECT_TYPE_RESOURCE))
    cur.append(('$uri','"%s"'%Schema.STYLESHEET_DEPENDENCY,"//xsl:import/@href", OBJECT_TYPE_RESOURCE))
    del cur

    #Commands
    cur = systemDocDefs[ResourceTypes.ResourceType.COMMAND][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.COMMAND][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.COMMAND_NAME,"/ftss:Command/@name", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.COMMAND_FULL_NAME,"/ftss:Command/@full-name", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.COMMAND_SUBCOMMAND,"/ftss:Command/ftss:SubCommands/ftss:CommandReference/@xlink:href", OBJECT_TYPE_RESOURCE))
    del cur

    #Server
    cur = systemDocDefs[ResourceTypes.ResourceType.SERVER][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.SERVER][ResourceTypes.RESOURCE_CONTENT] = systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri','"%s"'%Schema.SERVER_NAME,"/ftss:Server/@name", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.SERVER_HANDLER,"/ftss:Server/ftss:Handler", OBJECT_TYPE_LITERAL))
    cur.append(('$uri','"%s"'%Schema.SERVER_RUNNING,"/ftss:Server/ftss:Status/@running", OBJECT_TYPE_LITERAL))
    del cur


    #RDF Documents
    cur = systemDocDefs[ResourceTypes.ResourceType.RDF_DOCUMENT][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.RDF_DOCUMENT][ResourceTypes.RESOURCE_CONTENT]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    del cur

    #Schematron Documents
    cur = systemDocDefs[ResourceTypes.ResourceType.SCHEMATRON_DOCUMENT][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.SCHEMATRON_DOCUMENT][ResourceTypes.RESOURCE_CONTENT]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    del cur

    #Alias
    cur = systemDocDefs[ResourceTypes.ResourceType.ALIAS][ResourceTypes.RESOURCE_METADATA]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_METADATA][:]
    del cur

    cur = systemDocDefs[ResourceTypes.ResourceType.ALIAS][ResourceTypes.RESOURCE_CONTENT]= systemDocDefs[ResourceTypes.ResourceType.XML_DOCUMENT][ResourceTypes.RESOURCE_CONTENT][:]
    cur.append(('$uri', '"%s"'%Schema.ALIAS_REFERENCE, "/ftss:Alias/@reference", OBJECT_TYPE_RESOURCE))
    del cur


    #Compile the system docdefs
    _systemDocDefs = {}
    for typ, modes in systemDocDefs.items():
        _systemDocDefs[typ] = {}

        for mode, exprs in modes.items():
            _systemDocDefs[typ][mode] = []
            for s, p, o, t in exprs:
                _systemDocDefs[typ][mode].append(
                    (XPath.Compile(s), XPath.Compile(p), XPath.Compile(o), t)
                    )
    del typ, modes
    del systemDocDefs




def RecurPrint(obj, done=[]):
    import types
    if type(obj) is types.InstanceType:
        done.append(obj)
        print "Instance: %s" % repr(obj)
        for name,value in obj.__dict__.items():
            print "Class Member %s:" % name
            if value in done:
                repr(obj)
            else:
                RecurPrint(value)
    elif type(obj) == type(FtssDriver):
        print repr(obj)
        print obj
        raise obj
    else:
        print repr(obj)



