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
18
19
20
21
22
23
24
25 ADVENE_TYPE = RELATION
26
27
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=()):
43 model_id = PackageElement._check_reference(owner, model, RESOURCE)
44 cls._check_content_cls(mimetype, model_id, url)
45 owner._backend.create_relation(owner._id, id, mimetype, model_id, url)
46 r = cls.instantiate(owner, id, mimetype, model_id, url)
47 if members:
48 r.extend(members)
49 return r
50
66
69
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
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
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
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]
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
119
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
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
146
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
164
165
167
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
186
187
188 - def extend(self, annotations):
191
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
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
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]
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
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]
239 r = self._ids[i] = o._backend.get_member(o._id, self._id, i)
240 return r
241
242
243