754231f151
svn: r5872
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
|
|
import cPickle
|
|
import logging
|
|
log = logging.getLogger(".")
|
|
|
|
class ListCursor(object):
|
|
"""
|
|
Provides a wrapper around the cursor class that provides fast
|
|
traversal using treeview paths for models that are LISTONLY, i.e.
|
|
they have no tree structure.
|
|
|
|
It keeps track of the current index that the cursor is pointing
|
|
at.
|
|
|
|
@ivar _index: The current index pointed to by the cursor.
|
|
|
|
To speed up lookups the cursor is kept as close as possible to the
|
|
likely next lookup and is moved using next()/prev() were ever
|
|
possible.
|
|
|
|
@ivar _object_cache: A cache of previously fetched records. These are
|
|
indexed by the values of the L{_index}.
|
|
"""
|
|
|
|
def __init__(self,cursor):
|
|
"""
|
|
@param cursor: The cursor used to fetch the records.
|
|
@type cursor: An object supporting the cursor methods of a U{BSDB
|
|
cursor<http://pybsddb.sourceforge.net/bsddb3.html>}.
|
|
It must have a BTREE index type and DB_DUP to support duplicate
|
|
records. It should probably also have DB_DUPSORT if you want to
|
|
have sorted records.
|
|
"""
|
|
self._cursor = cursor
|
|
self._object_cache = {}
|
|
|
|
self.top()
|
|
|
|
|
|
def top(self):
|
|
self._cursor.first()
|
|
self._index = 0
|
|
|
|
def next(self):
|
|
"""
|
|
Move to the next record.
|
|
"""
|
|
data = self._cursor.next()
|
|
|
|
# If there was a next record that data will
|
|
# not be None
|
|
if data is not None:
|
|
# Up date the index pointers so that
|
|
# they point to the current record.
|
|
self._index+= 1
|
|
|
|
return data
|
|
|
|
def prev(self):
|
|
"""
|
|
Move to the previous record.
|
|
"""
|
|
data = self._cursor.prev()
|
|
|
|
# If there was a next record that data will
|
|
# not be None
|
|
if data is not None:
|
|
# Up date the index pointers so that
|
|
# they point to the current record.
|
|
self._index -= 1
|
|
|
|
return data
|
|
|
|
|
|
def has_children(self,path):
|
|
"""
|
|
This cursor is only for simple lists so no records have
|
|
children.
|
|
|
|
@param path: The path spec to check.
|
|
@type path: A TreeView path.
|
|
"""
|
|
|
|
return False
|
|
|
|
def get_n_children(self,path):
|
|
"""
|
|
Return the number of children that the record at I{path} has.
|
|
|
|
@param path: The path spec to check.
|
|
@type path: A TreeView path.
|
|
"""
|
|
|
|
return 0
|
|
|
|
def lookup(self,index,use_cache=True):
|
|
"""
|
|
Lookup a primary record.
|
|
|
|
@param index: The index of the primary record. This is its
|
|
possition in the sequence of non_duplicate keys.
|
|
@type index: int
|
|
@para use_case: If B{True} the record will be looked up in the
|
|
object cache and will be returned from there. This will not
|
|
update the possition of the cursor. If B{False} the record will
|
|
fetched from the cursor even if it is in the object cache and
|
|
cursor will be left possitioned on the record.
|
|
"""
|
|
|
|
# See if the record is in the cache.
|
|
if self._object_cache.has_key(index) and use_cache is True:
|
|
ret = self._object_cache[index]
|
|
|
|
# If the record is not in the cache or we are ignoring the
|
|
# cache.
|
|
else:
|
|
|
|
# If the cursor points to the record we want
|
|
# the first index will be equal to the
|
|
# index required
|
|
if index == self._index:
|
|
ret = self._cursor.current()
|
|
|
|
# If the current cursor is behind the
|
|
# requested index move it forward.
|
|
elif index < self._index:
|
|
while index < self._index:
|
|
ret = self.prev()
|
|
if ret is None:
|
|
log.warn("Failed to move back to index = %s" % (str(index)))
|
|
break
|
|
|
|
ret = self._cursor.current()
|
|
|
|
# If the current cursor is in front of
|
|
# requested index move it backward.
|
|
else:
|
|
while index > self._index:
|
|
ret = self.next()
|
|
if ret is None:
|
|
log.warn("Failed to move forward to index = %s" % (str(index)))
|
|
break
|
|
|
|
ret = self._cursor.current()
|
|
|
|
# when we have got the record save it in
|
|
# the cache
|
|
if ret is not None:
|
|
ret = self._unpickle(ret)
|
|
self._object_cache[index] = ret
|
|
|
|
return ret
|
|
|
|
def _unpickle(self,rec):
|
|
"""
|
|
It appears that reading an object from a cursor does not
|
|
automatically unpickle it. So this method provides
|
|
a convenient way to unpickle the object.
|
|
"""
|
|
if rec and type(rec[1]) == type(""):
|
|
tmp = [rec[0],None]
|
|
tmp[1] = cPickle.loads(rec[1])
|
|
rec = tmp
|
|
return rec
|
|
|
|
def lookup_path(self,path):
|
|
"""
|
|
Lookup a record from a patch specification.
|
|
|
|
@param path: The path spec to check.
|
|
@type path: A TreeView path.
|
|
|
|
"""
|
|
return self.lookup(path[0])
|
|
|
|
|