########################################################################
#
# File Name:            Memory.py
#
# Documentation:        http://docs.4suite.org/4Rdf/Drivers/Memory.py.html
#
"""
Non-persistent RDF model back-end
WWW: http://4suite.org/4RDF         e-mail: support@4suite.org

Copyright (c) 1999,2000 Fourthought Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""


import string, re, pcre

from Ft.Rdf.Drivers import DataBaseExceptions
from Ft.Rdf.Statement import Statement
from Ft.Rdf import Model, RdfException
from Ft.Lib import Set

def CreateDb(dbName):
    return DbAdapter(dbName)

#Identical function for Memory driver
GetDb = CreateDb

def DestroyDb(dbName):
    pass

def ExistsDb(dbName):
    return 0


class DbAdapter:
    def __init__(self, name):
        self._db = 0
        self._acl = {}
        self._statements = []
        self._bound = {}

    ### Transactional Interface ###

    def begin(self):
        self._db = 1
        return

    def commit(self):
        self._db = 0
        return

    def rollback(self):
        self._db = 0
        return

    ### Operations ###

    def add(self, statements):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        # stored statements -> statement tuple
        self._statements.extend(statements)
        return

    def remove(self, statements):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        for s in statements:
            self.removePattern(s[0], s[1], s[2], s[3], s[4], {})
        return

    def removePattern(self, subject, predicate, object, statementUri,
                      sourceUri, flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction

        # we use not not because '' and None are equivelant to us
        command = g_removes[(not not subject,
                             not not predicate,
                             not not object,
                             not not statementUri,
                             not not sourceUri,
                             )]
        operators = (g_operators[flags.get('subjectFlags')](subject or ''),
                     g_operators[flags.get('predicateFlags')](predicate or ''),
                     g_operators[flags.get('objectFlags')](object or ''),
                     g_operators[flags.get('statementUriFlags')](statementUri or ''),
                     g_operators[flags.get('sourceUriFlags')](sourceUri or ''),
                     )
        self._statements = filter(lambda s, f=command, o=operators:
                                  not f(o, s),
                                  self._statements)
        return

    ### Queries

    def complete(self, subject, predicate, object, statementUri, sourceUri,
                 flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        command = g_completes[(not not subject,
                               not not predicate,
                               not not object,
                               not not statementUri,
                               not not sourceUri,
                               )]
        operators = (g_operators[flags.get('subjectFlags')](subject or ''),
                     g_operators[flags.get('predicateFlags')](predicate or ''),
                     g_operators[flags.get('objectFlags')](object or ''),
                     g_operators[flags.get('statementUriFlags')](statementUri or ''),
                     g_operators[flags.get('sourceUriFlags')](sourceUri or ''),
                     )
                               
        return filter(lambda s, f=command, o=operators:
                      f(o, s),
                      self._statements)

    def size(self, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        if sourceUri:
            return reduce(lambda r, s, u=sourceUri:
                          r + (s[4] == u),
                          self._statements, 0)
        else:
            return len(self._statements)

    def contains(self, subject, predicate, object, statementUri, sourceUri, flags):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        command = g_contains[(not not subject,
                              not not predicate,
                              not not object,
                              not not statementUri,
                              not not sourceUri,
                              )]
        operators = (g_operators[flags.get('subjectFlags')](subject or ''),
                     g_operators[flags.get('predicateFlags')](predicate or ''),
                     g_operators[flags.get('objectFlags')](object or ''),
                     g_operators[flags.get('statementUriFlags')](statementUri or ''),
                     g_operators[flags.get('sourceUriFlags')](sourceUri or ''),
                     )

        size = reduce(lambda r, s, f=command, o=operators:
                      r + (f(o, s) and 1 or 0),
                      self._statements, 0)
        return size > 0

    def bind(self, object, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        
        if not self._bound.has_key(sourceUri):
            self._bound[sourceUri] = {}

        self._bound[sourceUri][name] = object

    def unbind(self, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        if not self._bound.has_key(sourceUri):
            return

        info = self._bound[sourceUri].get(name)
        if info:
            del self._bound[sourceUri][name]

    def lookup(self, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        if not self._bound.has_key(sourceUri):
            return None

        return self._bound[sourceUri].get(name)

    def keys(self, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        if not sourceUri:
            result = []
            for bindings in self._bound.values():
                result.extend(bindings.keys())
            return result
        else:
            if self._bound.has_key(sourceUri):
                result = self._bound[sourceUri].keys()
            else:
                result = []
        return result

    def has_key(self, name, sourceUri):
        if not self._db:
            raise DataBaseExceptions.NoTransaction
        if not sourceUri:
            result = reduce(lambda a, b, n=name:
                            a + b.has_key(n),
                            self._bound.values(), 0)
        else:
            if self._bound.has_key(sourceUri):
                result = self._bound[sourceUri].has_key(name)
            else:
                result = 0

        return result


def _regexCompile(cmd):
    try:
        return re.compile(cmd).match
    except re.error, e:
        raise RdfException(RdfException.INVALID_REGEX_STATEMENT, cmd, str(e))

def _regexICCompile(cmd):
    try:
        return re.compile(cmd,re.IGNORECASE).match
    except re.error, e:
        raise RdfException(RdfException.INVALID_REGEX_STATEMENT, cmd, str(e))

g_operators = {
    None : lambda s: lambda a, b=s: a == b,
    Model.IGNORE_CASE : lambda s: lambda a, b=string.lower(s): string.lower(a) == b,
    Model.REGEX : _regexCompile,
    Model.IGNORE_CASE + Model.REGEX : _regexICCompile,
    }

g_completes = {}
g_removes = {}
g_contains = {}

for bits in range(32):
    key = (bits & 16 > 0,
           bits & 8 > 0,
           bits & 4 > 0,
           bits & 2 > 0,
           bits & 1)

    # where f = comparision function for each item of a statement tuple
    #       s = the statement tuple

    parts = []
    if bits & 16:
        parts.append('f[0](s[0])')
    if bits & 8:
        parts.append('f[1](s[1])')
    if bits & 4:
        parts.append('f[2](s[2])')
    if bits & 2:
        parts.append('f[3](s[3])')
    if bits & 1:
        parts.append('f[4](s[4])')

    if parts:
        body = string.join(parts, ' and ')
    else:
        body = '1'

    g_completes[key] = eval('lambda f,s: %s' % body)
    g_removes[key] = eval('lambda f,s: %s' % body)

    if parts:
        body = string.join(parts, ' and ')
    else:
        body = '1'
    g_contains[key] = eval('lambda f,s: %s' % body)
