gramps/src/Models/_ListCursor.py
2006-02-03 15:49:59 +00:00

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])