Package advene :: Package model :: Package core :: Module relation
[hide private]
[frames] | no frames]

Source Code for Module advene.model.core.relation

  1  """ 
  2  I define the class of relations. 
  3  """ 
  4   
  5  from advene.model.consts import _RAISE 
  6  from advene.model.core.element \ 
  7    import PackageElement, ANNOTATION, RELATION, RESOURCE 
  8  from advene.model.core.content import WithContentMixin 
  9  from advene.model.core.group import GroupMixin 
10 11 -class Relation(PackageElement, WithContentMixin, GroupMixin):
12 """ 13 I expose the protocol of a basic collection, to give access to the members 14 of a relation. I also try to efficiently cache the results I know. 15 """ 16 17 # Caching is performed as follow: 18 # __init__ retrieves the number of members, and builds self.__ids 19 # and self.__cache, a list of id-refs and instances respectively. 20 # Whenever an index is accessed, the member if retrieved from self.__cache. 21 # If None, its id-ref is retrieved from self.__ids and the element is 22 # retrieved from the package. If the id-ref is None, the id-ref is 23 # retrieved from the backend. 24 25 ADVENE_TYPE = RELATION 26 27 # attributes that do not prevent relations to be volatile 28 _cache = None 29 _ids = None 30 31 @classmethod
32 - def instantiate(cls, owner, id, mimetype, model, url, *args):
33 r = super(Relation, cls). \ 34 instantiate(owner, id, mimetype, url, model, *args) 35 r._instantiate_content(mimetype, model, url) 36 c = owner._backend.count_members(owner._id, id) 37 r._cache = [None,] * c 38 r._ids = [None,] * c 39 return r
40 41 @classmethod
42 - def create_new(cls, owner, id, mimetype, model, url, members=()):
50
51 - def _update_caches(self, old_idref, new_idref, element, relation):
52 """ 53 :see-also: `advene.model.core.element.PackageElement._update_caches` 54 """ 55 if relation.startswith(":member "): 56 index = int(relation[8:]) 57 self._ids[index] = new_idref 58 if element is not None: 59 self._cache[index] = element # just in case both were None 60 else: 61 try: 62 super(Relation, self) \ 63 ._update_caches(old_idref, new_idref, element, relation) 64 except AttributeError: 65 pass
66
67 - def __len__(self):
68 return len(self._cache)
69
70 - def __iter__(self):
71 """Iter over the members of this relation. 72 73 If the relation contains unreachable members, None is yielded. 74 75 See also `iter_member_ids`. 76 """ 77 for i,m in enumerate(self._cache): 78 if m is None: 79 m = self.get_member(i, None) 80 yield m
81
82 - def __getitem__(self, i):
83 """Return member with index i, or raise an exception if the item is 84 unreachable. 85 86 See also `get_member` and `get_member_id`. 87 """ 88 if isinstance(i, slice): return self._get_slice(i) 89 else: return self.get_member(i, _RAISE)
90
91 - def __setitem__(self, i, a):
92 if isinstance(i, slice): return self._set_slice(i, a) 93 assert getattr(a, "ADVENE_TYPE", None) == ANNOTATION, "A relation member must be an Annotation" 94 o = self._owner 95 assert o._can_reference(a), "The relation owner %s cannot reference %s" % (str(o), str(a)) 96 97 aid = a.make_id_in(o) 98 s = slice(i, i+1) 99 L = [a,] 100 self.emit("pre-modified-items", s, L) 101 self._ids[i] = aid 102 self._cache[i] = a 103 o._backend.update_member(o._id, self._id, aid, i) 104 self.emit("modified-items", s, L)
105
106 - def __delitem__(self, i):
107 if isinstance(i, slice): return self._del_slice(i) 108 s = slice(i, i+1) 109 self.emit("pre-modified-items", s, []) 110 del self._ids[i] # also guarantees that is is a valid index 111 del self._cache[i] 112 o = self._owner 113 o._backend.remove_member(o._id, self._id, i) 114 self.emit("modified-items", s, [])
115
116 - def _get_slice(self, s):
117 c = len(self._cache) 118 return [ self.get_member(i, _RAISE) for i in range(c)[s] ]
119
120 - def _set_slice(self, s, annotations):
121 c = len(self._cache) 122 indices = range(c)[s] 123 same_length = (len(annotations) == len(indices)) 124 if s.step is None and not same_length: 125 self._del_slice(s) 126 insertpoint = s.start or 0 127 for a in annotations: 128 self.insert(insertpoint, a) 129 insertpoint += 1 130 else: 131 if not same_length: 132 raise ValueError("attempt to assign sequence of size %s to " 133 "extended slice of size %s" 134 % (len(annotations), len(indices))) 135 for i,j in enumerate(indices): 136 self.__setitem__(j, annotations[i])
137
138 - def _del_slice(self,s):
139 c = len(self._cache) 140 indices = range(c)[s] 141 indices.sort() 142 for offset, i in enumerate(indices): 143 del self[i-offset]
144
145 - def insert(self, i, a):
146 # this method accepts a strict id-ref instead of a real element 147 o = self._owner 148 assert o._can_reference(a), "The relation owner %s cannot reference %s" % (str(o), str(a)) 149 if hasattr(a, "ADVENE_TYPE"): 150 assert a.ADVENE_TYPE == ANNOTATION 151 aid = a.make_id_in(o) 152 else: 153 aid = unicode(a) 154 assert aid.find(":") > 0, "Expected *strict* id-ref" 155 a = None 156 c = len(self._cache) 157 if i > c : i = c 158 if i < -c: i = 0 159 if i < 0 : i += c 160 self._ids.insert(i,aid) 161 self._cache.insert(i,a) 162 o._backend.insert_member(o._id, self._id, aid, i, c)
163 # NB: it is important to pass to the backend the length c computed 164 # *before* inserting the member 165
166 - def append(self, a):
167 # this method accepts a strict id-ref instead of a real element 168 o = self._owner 169 assert o._can_reference(a), "The relation owner %s cannot reference %s" % (str(o), str(a)) 170 if hasattr(a, "ADVENE_TYPE"): 171 assert a.ADVENE_TYPE == ANNOTATION 172 aid = a.make_id_in(o) 173 else: 174 aid = unicode(a) 175 assert aid.find(":") > 0, "Expected *strict* id-ref" 176 a = None 177 c = len(self._cache) 178 s = slice(c,c) 179 L = [a,] 180 self.emit("pre-modified-items", s, L) 181 self._ids.append(aid) 182 self._cache.append(a) 183 o._backend.insert_member(o._id, self._id, aid, -1, c) 184 self.emit("modified-items", s, L)
185 # NB: it is important to pass to the backend the length c computed 186 # *before* appending the member 187
188 - def extend(self, annotations):
189 for a in annotations: 190 self.append(a)
191
192 - def iter_member_ids(self):
193 """Iter over the id-refs of the members of this relation. 194 195 See also `__iter__`. 196 """ 197 for i,m in enumerate(self._ids): 198 if m is not None: 199 yield m 200 else: 201 yield self.get_member_id(i)
202
203 - def get_member(self, i, default=None):
204 """Return element with index i, or default if it can not be retrieved. 205 206 The difference with self[i] is that, if the member is unreachable, 207 None is returned (or whatever value is passed as ``default``). 208 209 Note that if ``i`` is an invalid index, an IndexError will still be 210 raised. 211 212 See also `__getitem__` and `get_member_id`. 213 """ 214 # NB: internally, default can be passed _RAISE to force exceptions 215 assert isinstance(i, (int, long)), "The index must be an integer" 216 r = self._cache[i] 217 if r is None: 218 o = self._owner 219 rid = self._ids[i] 220 if rid is None: 221 c = len(self._cache) 222 i = xrange(c)[i] # check index and convert negative 223 rid = self._ids[i] = \ 224 o._backend.get_member(o._id, self._id, i) 225 r = self._cache[i] = o.get_element(rid, default) 226 return r
227
228 - def get_member_id(self, i):
229 """Return id-ref of the element with index i. 230 231 See also `__getitem__` and `get_member`. 232 """ 233 assert isinstance(i, (int, long)), "The index must be an integer" 234 r = self._ids[i] 235 if r is None: 236 o = self._owner 237 c = len(self._ids) 238 i = xrange(c)[i] # check index and convert negative 239 r = self._ids[i] = o._backend.get_member(o._id, self._id, i) 240 return r
241 242 # 243