############################################################################
#
# File Name: 		CollectionBase.py
#
# Documentation:	http://docs.ftsuite.com/4ODS/CollectionBase.py.html
#
"""
Base collection class.
WWW: http://4suite.org/4ODS         e-mail: support@4suite.org

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

from Ft.Ods.Collections import Iterator
from Ft.Ods.Collections import BidirectionalIterator
from Ft.Ods import Collection, Exception
from Ft.Ods.Exception import FtodsUnsupportedError
from Ft.Ods import Constants
Actions = Constants.CollectionChanges

class CollectionBase(Collection.Collection):

    InvalidCollectionType = Exception.InvalidCollectionType
    ElementNotFound = Exception.ElementNotFound

    def __init__(self,db,cid,subType,subTypeRepoId,values):

        self.__dict__['_subType'] = subType
        self.__dict__['_subTypeRepoId'] = subTypeRepoId

        if values:
            self.__dict__['_values'] = values.copy()
        else:
            self.__dict__['_values'] = {}

        self.__dict__['_dbChanges'] = []

        Collection.Collection.__init__(self,db,cid)



    def __getattr__(self,name):
        if name == 'value':
            keys = self._values.keys()
            keys.sort()
            return map(lambda x,f = self._4ods_prepareValue,v=self._values:f(v[x]),keys)
        raise AttributeError(name)

    def _4ods_getChanges(self):
        return map(lambda x,f = self._4ods_prepareChange:f(x),self._dbChanges)

    def _4ods_getSubType(self):
        return self.__dict__['_subType']

    def _4ods_getSubRepositoryId(self):
        return self.__dict__['_subTypeRepoId']

    def _4ods_isModified(self):
        return len(self._dbChanges) > 0

    def _4ods_setTransaction(self, tx, unregister=0):
        CollectionBase._4ods_setTransaction(self, tx, unregister)
        if tx:
            self.__dict__['_dbChanges'] = []
        return


    #The our pythonic interface (defined first )
    def __cmp__(self, other):
        if isinstance(other, CollectionBase):
            if self._4ods_getId() and other._4ods_getId():
                return cmp(self._4ods_getId(), other._4ods_getId())
            return cmp(self[:], other[:])
        else:
            return cmp(self[:], other)

    def __len__(self): return len(self._values)

    def __getitem__(self, i):
        if i < 0: i = len(self._values.keys()) + i
        if not self._values.has_key(i):
            raise IndexError("Invalid Index")
        return self._4ods_prepareValue(self._values[i])

    def __getslice__(self, i, j):
        i = max(i, 0); j = min(j, self.cardinality())
        res = []
        for ctr in range(i,j):
            res.append(self[ctr])
        return res

    #The collection interface
    cardinality = __len__

    def is_empty(self):
        return len(self._values.keys()) == 0

    def is_ordered(self):
        return 0

    def allows_duplicates(self):
        return 0

    def contains_element(self, element):
        el = self._4ods_unprepareValue(element)
        return el in self._values.values()

    def insert_element(self, element):
        newIndex = len(self._values.keys())
        el = self._4ods_unprepareValue(element)
        self._values[newIndex] = el
        self._4ods_addChange(Actions.APPEND,newIndex,el)

    def remove_element(self, element):
        el = self._4ods_unprepareValue(element)
        for i,v in self._values.items():
            if v == el:
                del self._values[i]
                self._4ods_addChange(Actions.REMOVE,i,v)
                break
        else:
            raise self.ElementNotFound(element)

        values = {}
        for index,v in self._values.items():
            if index >= i:
                index = index - 1
            values[index] = v
        self._values = values

    def copy(self,deep=1):
        other = apply(self.__class__,(self._db,None,self._subType,self.__dict__['_subTypeRepoId'],None))
        if deep:
            for v in self:
                other.insert_element(v)
        return other

    def create_iterator(self, stable):
        return Iterator.Iterator(self, stable)

    def create_bidirectional_iterator(self, stable):
        return BidirectionalIterator.BidirectionalIterator(self, stable)

    def select_element(self,OQL_predicate):
        raise FtodsUnSupportedException(feature="select_element")

    def select(self,OQL_predicate):
        raise FtodsUnsupportedException(feature="select")

    def query(self,OQL_predicate):
        raise FtodsUnsupportedException(feature="select_element")

    def exists_element(self,OQL_predicate):
        raise FtodsUnsupportedException(feature="select_element")

    def _pseudo_del(self):
        self.__dict__['_values'] = []
    

    def _4ods_addChange(self,cmd,index,value):
        if len(self._dbChanges) and self._dbChanges[-1][0] == cmd:
            self._dbChanges[-1][1].append((value,index))
        else:
            self._dbChanges.append((cmd,[(value,index)]))



class FullPythonicCollectionBase(CollectionBase):


    def __setitem__(self,index,element):
        if index < 0: index = len(self._values.keys()) + index

        if self._values.has_key(index):
            #Replace
            old = self._values[index]
            self._4ods_addChange(Actions.REMOVE,index,old)
            el = self._4ods_unprepareValue(element)
            self._values[index] = el
            self._4ods_addChange(Actions.INSERT,index,el)
        else:
            CollectionBase.insert_element(self,element)



