X-Git-Url: https://scm.cri.ensmp.fr/git/Plinn.git/blobdiff_plain/99afe0ce8700bf835ff7bbedf77bb46e58f996d8..0a421746c0aa1a7d8b9ea7546a5ae50caf42922c:/HugePlinnFolder.py diff --git a/HugePlinnFolder.py b/HugePlinnFolder.py index d8f33fd..6f971b4 100644 --- a/HugePlinnFolder.py +++ b/HugePlinnFolder.py @@ -30,113 +30,201 @@ from BTrees.IOBTree import IOBTree from BTrees.OIBTree import OIBTree from Folder import PlinnFolder from zope.event import notify -from zope.app.container.contained import notifyContainerModified +try : + from zope.app.container.contained import notifyContainerModified +except ImportError : + ## Zope-2.13 compat + from zope.container.contained import notifyContainerModified from events import ObjectPositionModified from zope.component.factory import Factory from Products.CMFCore.permissions import AddPortalFolders, \ - ManageProperties, \ - AccessContentsInformation + ManageProperties, \ + AccessContentsInformation from AccessControl import ClassSecurityInfo from Globals import InitializeClass from types import StringType class HugePlinnFolder(BTreeFolder2Base, PlinnFolder) : - """ Plinn Folder for large set of objects - """ - - security = ClassSecurityInfo() - - def __init__(self, id, title='') : - PlinnFolder.__init__(self, id, title) - BTreeFolder2Base.__init__(self, id) - - def _initBTrees(self): - super(HugePlinnFolder, self)._initBTrees() - self._pos2id_index = IOBTree() - self._id2pos_index = OIBTree() - - def _checkId(self, id, allow_dup=0) : - PlinnFolder._checkId(self, id, allow_dup) - BTreeFolder2Base._checkId(self, id, allow_dup) - - security.declareProtected(AddPortalFolders, 'manage_addHugePlinnFolder') - def manage_addHugePlinnFolder(self, id, title='', REQUEST=None) : - """ Add new a new HugePlinnFolder object with id *id*. - """ - ob = HugePlinnFolder(id, title) - self._setObject(id, ob) - if REQUEST is not None : - return self.folder_contents(self, REQUEST, portal_status_message='Folder added') - - def _setOb(self, id, object): - super(HugePlinnFolder, self)._setOb(id, object) - pos = self.objectCount() - 1 - self._pos2id_index[pos] = id - self._id2pos_index[id] = pos - - def _delOb(self, id): - pos = self._id2pos_index[id] - self._id2pos_index.pop(id) - - for p in xrange(pos+1, self.objectCount()) : - ident = self._pos2id_index[p] - self._pos2id_index[p-1] = ident - self._id2pos_index[ident] = p-1 - - self._pos2id_index.pop(self.objectCount()-1) - - super(HugePlinnFolder, self)._delOb(id) - - security.declareProtected(AccessContentsInformation, 'objectIds') - def objectIds(self, spec=None) : - if spec is not None : - return super(HugePlinnFolder, self).objectIds(spec) - - pos2id = lambda pos : self._pos2id_index[pos] - return LazyMap(pos2id, xrange(self.objectCount())) - - - - security.declareProtected(ManageProperties, 'moveObjectsByDelta') - def moveObjectsByDelta(self, ids, delta, subset_ids=None, - suppress_events=False): - """ Move specified sub-objects by delta. - """ - if isinstance(ids, StringType): - ids = (ids,) - - id2pos = self._id2pos_index - pos2id = self._pos2id_index - for id in ids : - oldPosition = id2pos[id] - newPosition = max(oldPosition + delta, 0) - - shift = delta > 0 and 1 or -1 - for p in xrange(oldPosition, newPosition, shift) : - ident = pos2id[p+shift] - pos2id[p] = ident - id2pos[ident] = p - if not suppress_events : - notify(ObjectPositionModified(self[ident], self, p)) - - id2pos[id] = newPosition - pos2id[newPosition] = id - if not suppress_events : - notify(ObjectPositionModified(self[id], self, newPosition)) - - if not suppress_events : - notifyContainerModified(self) - - - def getObjectPosition(self, id): - """ Get the position of an object by its id. - """ - try : - return self._id2pos_index[id] - except KeyError : - raise ValueError('The object with the id "%s" does not exist.' % id) - + """ Plinn Folder for large set of objects + """ + + security = ClassSecurityInfo() + + __getitem__ = PlinnFolder.__getitem__ + + def __init__(self, id, title='') : + PlinnFolder.__init__(self, id, title) + BTreeFolder2Base.__init__(self, id) + + def _initBTrees(self): + super(HugePlinnFolder, self)._initBTrees() + self._pos2id_index = IOBTree() + self._id2pos_index = OIBTree() + + def _checkId(self, id, allow_dup=0) : + PlinnFolder._checkId(self, id, allow_dup) + BTreeFolder2Base._checkId(self, id, allow_dup) + + security.declareProtected(AddPortalFolders, 'manage_addHugePlinnFolder') + def manage_addHugePlinnFolder(self, id, title='', REQUEST=None) : + """ Add new a new HugePlinnFolder object with id *id*. + """ + ob = HugePlinnFolder(id, title) + self._setObject(id, ob) + if REQUEST is not None : + return self.folder_contents(self, REQUEST, portal_status_message='Folder added') + + def _setOb(self, id, object): + super(HugePlinnFolder, self)._setOb(id, object) + pos = self.objectCount() - 1 + self._pos2id_index[pos] = id + self._id2pos_index[id] = pos + + def _delOb(self, id): + pos = self._id2pos_index[id] + self._id2pos_index.pop(id) + + for p in xrange(pos+1, self.objectCount()) : + ident = self._pos2id_index[p] + self._pos2id_index[p-1] = ident + self._id2pos_index[ident] = p-1 + + self._pos2id_index.pop(self.objectCount()-1) + + super(HugePlinnFolder, self)._delOb(id) + + security.declareProtected(AccessContentsInformation, 'objectIds') + def objectIds(self, spec=None) : + if spec is not None : + return super(HugePlinnFolder, self).objectIds(spec) + + pos2id = lambda pos : self._pos2id_index[pos] + return LazyMap(pos2id, xrange(self.objectCount())) + + + + security.declareProtected(ManageProperties, 'moveObjectsByDelta') + def moveObjectsByDelta(self, ids, delta, subset_ids=None, + suppress_events=False): + """ Move specified sub-objects by delta. + """ + if isinstance(ids, StringType): + ids = (ids,) + + id2pos = self._id2pos_index + pos2id = self._pos2id_index + for id in ids : + oldPosition = id2pos[id] + newPosition = max(oldPosition + delta, 0) + + shift = delta > 0 and 1 or -1 + for p in xrange(oldPosition, newPosition, shift) : + ident = pos2id[p+shift] + pos2id[p] = ident + id2pos[ident] = p + if not suppress_events : + notify(ObjectPositionModified(self[ident], self, p)) + + id2pos[id] = newPosition + pos2id[newPosition] = id + if not suppress_events : + notify(ObjectPositionModified(self[id], self, newPosition)) + + if not suppress_events : + notifyContainerModified(self) + + security.declareProtected(ManageProperties, 'moveObjectsAfter') + def moveObjectsAfter(self, ids, targetId, suppress_events=False): + assert targetId not in ids + + # id2pos = dict(self._id2pos_index).copy() + # pos2id = dict(self._pos2id_index).copy() + id2pos = self._id2pos_index + pos2id = self._pos2id_index + targetPos = id2pos[targetId] + minMovedPos = min([id2pos[id] for id in ids]) + maxMovedPos = max([id2pos[id] for id in ids]) + + for id in ids : + assert id == pos2id.pop(id2pos.pop(id)) + + id2posUpdate = {} + pos2idUpdate = {} + + if targetPos < minMovedPos : + # selection moved before the first item position + for i, id in enumerate(ids) : + pos = i + targetPos + 1 + id2posUpdate[id] = pos + pos2idUpdate[pos] = id + + for id in IndexIterator(pos2id, targetPos+1, maxMovedPos): + pos = pos + 1 + id2posUpdate[id] = pos + pos2idUpdate[pos] = id + + elif targetPos > minMovedPos and targetPos < maxMovedPos : + print minMovedPos, maxMovedPos, targetPos + print "déposé entre la première et la dernière de la sélection" + raise NotImplementedError() + else : + # selection moved after the last item position + pos = minMovedPos + for id in IndexIterator(pos2id, minMovedPos+1, targetPos) : + id2posUpdate[id] = pos + pos2idUpdate[pos] = id + pos += 1 + + pos = targetPos - len(ids) + 1 + for id in ids : + id2posUpdate[id] = pos + pos2idUpdate[pos] = id + pos +=1 + + id2pos.update(id2posUpdate) + pos2id.update(pos2idUpdate) + + # just for debug + for pos in xrange(len(self)) : + assert pos2id.has_key(pos) + assert id2pos.has_key(pos2id[pos]) + + if not suppress_events : + for id, pos in id2posUpdate.items() : + notify(ObjectPositionModified(self[id], self, pos)) + + notifyContainerModified(self) + + def getObjectPosition(self, id): + """ Get the position of an object by its id. + """ + try : + return self._id2pos_index[id] + except KeyError : + raise ValueError('The object with the id "%s" does not exist.' % id) + + +class IndexIterator : + def __init__(self, d, start, stop) : + self.d = d + self.pos = start + self.stop = stop + + def __iter__(self) : + return self + + def next(self) : + try : + if self.pos > self.stop : + raise StopIteration + v = self.d[self.pos] + self.pos = self.pos + 1 + return v + except KeyError : + self.pos = self.pos + 1 + return self.next() + InitializeClass(HugePlinnFolder) HugePlinnFolderFactory = Factory(HugePlinnFolder)