source: trunk/src/model/changes.lisp

Last change on this file was 977, checked in by lgiessmann, 13 years ago

trunk: merged branches/gdl-frontend with trunk; fixed all conflicts

  • Property svn:eol-style set to native
File size: 18.3 KB
Line 
1;;+-----------------------------------------------------------------------------
2;;+  Isidorus
3;;+  (c) 2008-2010 Marc Kuester, Christoph Ludwig, Lukas Georgieff
4;;+
5;;+  Isidorus is freely distributable under the LLGPL license.
6;;+  You can find a detailed description in trunk/docs/LLGPL-LICENSE.txt and
7;;+  trunk/docs/LGPL-LICENSE.txt.
8;;+-----------------------------------------------------------------------------
9
10(in-package :datamodel)
11
12(defun get-all-revisions ()
13  "Returns an ordered set of the start dates of all revisions in the engine"
14  ;TODO: this is a very inefficient implementation... it would equally
15  ;be possible to have a separate object that stored all such
16  ;revisions and only make the search from the latest version that's
17  ;stored their
18  (let ((revision-set))
19    (dolist (vi (elephant:get-instances-by-class 'VersionInfoC))
20      (pushnew (start-revision vi) revision-set))
21    (sort revision-set #'<)))
22
23
24(defun get-all-revisions-for-tm (tm-id)
25  "Returns an ordered set of the start dates of all revisions in the
26engine for this Topic Map"
27  (let*
28      ((tm (get-item-by-item-identifier tm-id :revision 0))
29       (tops-and-assocs (when tm (union (topics tm) (associations tm))))
30       (revision-set nil))
31    (dolist (vi (mapcan #'versions tops-and-assocs))
32      (pushnew (start-revision vi) revision-set))
33    (sort revision-set #'<)))
34
35
36(defgeneric find-all-associations (instance &key revision)
37  (:documentation "Finds all associations for a topic.")
38  (:method ((instance TopicC) &key (revision *TM-REVISION*))
39    (declare (type (or integer null) revision))
40    (delete-if #'null
41               (remove-duplicates 
42                (map 'list #'(lambda(role)
43                               (parent role :revision revision))
44                     (player-in-roles instance :revision revision))))))
45
46
47(defgeneric find-associations (instance &key revision)
48  (:documentation "Finds all associations of this topic except
49                   type-instance-associations.")
50  (:method ((instance TopicC) &key (revision *TM-REVISION*))
51    (declare (type (or integer null) revision))
52    (let ((type-instance-topic
53           (d:identified-construct
54            (elephant:get-instance-by-value
55             'PersistentIdC 'uri *type-instance-psi*))))
56      (delete-if
57       #'(lambda(assoc)
58           (eql (instance-of assoc :revision revision)
59                type-instance-topic))
60       (find-all-associations instance :revision revision)))))
61 
62
63(defgeneric find-referenced-topics (construct &key revision)
64  (:documentation "find all the topics that are references from this construct as type, scope or player, as the case may be"))
65
66
67(defmethod find-referenced-topics ((characteristic CharacteristicC)
68                                   &key (revision *TM-REVISION*))
69  "Characteristics are scopable + typable + reifiable.
70   Note the tmdm:topic-name is ignored if it is only set
71   as a nametype."
72  (append
73   (when (reifier characteristic :revision revision)
74     (list (reifier characteristic :revision revision)))
75   (themes characteristic :revision revision)
76   (when (and (not (and (typep characteristic 'NameC)
77                        (eql (instance-of characteristic :revision revision)
78                             (get-item-by-psi *topic-name-psi* :revision revision))))
79              (instance-of characteristic :revision revision))
80     (list (instance-of characteristic :revision revision)))
81   (when (and (typep characteristic 'NameC)
82              (variants characteristic :revision revision))
83     (delete-if #'null
84                (loop for var in (variants characteristic :revision revision)
85                   append (find-referenced-topics var :revision revision))))
86   (when  (and (typep characteristic 'OccurrenceC)
87              (> (length (charvalue characteristic)) 0)
88              (eq #\# (elt (charvalue characteristic) 0)))
89     (list (get-item-by-id (subseq (charvalue characteristic)  1)
90                           :revision revision)))))
91
92
93(defmethod find-referenced-topics ((role RoleC)
94                                   &key (revision *TM-REVISION*))
95  (append
96   (when (reifier role :revision revision)
97     (list (reifier role :revision revision)))
98   (list (instance-of role :revision revision))
99   (list (player role :revision revision))))
100
101
102(defmethod find-referenced-topics ((association AssociationC)
103                                   &key (revision *TM-REVISION*))
104  "associations are scopable + typable"
105  (append
106   (when (reifier association :revision revision)
107     (list (reifier association :revision revision)))
108   (list (instance-of association :revision revision))
109   (themes association :revision revision)
110   (mapcan #'(lambda(role)
111               (find-referenced-topics role :revision revision))
112           (roles association :revision revision))))
113 
114
115(defmethod find-referenced-topics ((top TopicC)
116                                   &key (revision *TM-REVISION*))
117  "Part 1b of the eGov-Share spec states:
118# for each topicname in T export a topic stub for each scope topic
119# for each occurrence in T export a topic stub for the occurrence type (if it exists)
120# for each occurrence in T export a topic stub for each scope topic
121# for each association A in which T plays a role export the association
122# for each association A export a topic stub for the association type
123# for each association A export a topic stub for each topic scope topic
124# for each role R in A export a topic stub for the role type and one for the role player UNLESS the role player is T"
125  (remove-duplicates
126   (remove
127    top
128    (append
129     (list-instanceOf top :revision revision)
130     (mapcan #'(lambda(name)
131                 (find-referenced-topics name :revision revision))
132             (names top :revision revision))
133     (mapcan #'(lambda(variant)
134                 (find-referenced-topics variant :revision revision))
135             (mapcan #'variants (names top :revision revision)))
136     (mapcan #'(lambda(occ)
137                 (find-referenced-topics occ :revision revision))
138             (occurrences top :revision revision))
139     (mapcan #'(lambda(assoc)
140                 (find-referenced-topics assoc :revision revision))
141             (find-associations top :revision revision))))))
142   
143
144(defgeneric initial-version-p (version-info)
145  (:documentation "A helper function for changed-p that returns the passed
146                   version-info object if it is the initial version-info object,
147                   i.e. it owns the smallest start-revsion of the
148                   version-construct.")
149  (:method ((version-info VersionInfoC))
150    (unless (find-if #'(lambda(vi)
151                         (< (start-revision vi) (start-revision version-info)))
152                     (versions (versioned-construct version-info)))
153      version-info)))
154
155
156(defgeneric changed-p (construct revision)
157  (:documentation "Has the topic map construct changed in a given revision?
158                   'Changed' can mean:
159    * newly created
160    * deletion of an element
161    * modified through the addition or removal of identifiers
162    * (for associations) modified through the addition or removal of
163       identifiers in the association or one of its roles
164    * (for topics) modified through the addition or removal of identifiers
165       or characteristics
166    * (for topics) modified through the addition or removal of an association
167       in which it is first player"))
168
169
170(defmethod changed-p ((construct TopicMapConstructC) (revision integer))
171  "changed-p returns nil for TopicMapConstructCs that are not specified
172   more detailed. The actual algorithm is processed for all
173   VersionedConstructCs."
174  (declare (ignorable revision))
175  nil)
176
177
178(defmethod changed-p ((construct PointerC) (revision integer))
179  "Returns t if the PointerC was added to a construct the first
180   time in the passed revision"
181  (let ((version-info (some #'(lambda(pointer-association)
182                                (changed-p pointer-association revision))
183                            (slot-p construct 'identified-construct))))
184    (when version-info
185      (initial-version-p version-info))))
186
187
188(defmethod changed-p ((construct VersionedConstructC) (revision integer))
189  "changed-p returns t if there exist a VersionInfoC with the given start-revision."
190  (let ((version-info
191         (find revision (versions construct) :test #'= :key #'start-revision)))
192    (when version-info
193      (initial-version-p version-info))))
194
195
196(defmethod changed-p ((construct CharacteristicC) (revision integer))
197  "Returns t if the CharacteristicC was added to a construct in the passed
198   revision or if <ReifiableConstructC> changed."
199  (or (call-next-method)
200      (let ((version-info
201             (some #'(lambda(characteristic-association)
202                       (changed-p characteristic-association revision))
203                   (slot-p construct 'parent))))
204        (when version-info
205          (initial-version-p version-info)))))
206
207
208(defmethod changed-p ((construct RoleC) (revision integer))
209  "Returns t if the RoleC was added to a construct in the passed
210   revision or if <ReifiableConstructC> changed."
211  (or (call-next-method)
212      (let ((version-info
213             (some #'(lambda(role-association)
214                       (changed-p role-association revision))
215                   (slot-p construct 'parent))))
216        (when version-info
217          (initial-version-p version-info)))))
218
219
220(defgeneric end-revision-p (construct revision)
221  (:documentation "A helper function for changed-p. It returns the latest
222                   version-info if the passed versioned-construct was
223                   marked-as-deleted in the version that is given.")
224  (:method ((construct VersionedConstructC) (revision integer))
225    (let ((version-info (find revision (versions construct)
226                              :key #'end-revision :test #'=)))
227      (when (and version-info
228                 (not
229                  (find-if
230                   #'(lambda(vi)
231                       (or (> (end-revision vi) (end-revision version-info))
232                           (= (end-revision vi) 0)))
233                   (versions construct))))
234        version-info))))
235
236
237(defmethod changed-p ((construct ReifiableConstructC) (revision integer))
238  "Returns t if a ReifiableConstructC changed in the given version, i.e.
239   an item-identifier or reifier was added to the construct itself."
240  (or (some #'(lambda(vc)
241                (changed-p vc revision))
242            (union (item-identifiers construct :revision revision)
243                   (let ((reifier-top (reifier construct :revision revision)))
244                     (when reifier-top
245                       (list reifier-top)))))
246      (some #'(lambda(vc)
247                (end-revision-p vc revision))
248            (union (slot-p construct 'item-identifiers)
249                   (slot-p construct 'reifier)))))
250
251
252(defmethod changed-p ((construct NameC) (revision integer))
253  "Returns t if the passed NameC changed in the given version, i.e.
254   the <ReifiableConstructC> characteristics or the variants changed."
255  (or (call-next-method)
256      (some #'(lambda(var)
257                (changed-p var revision))
258            (variants construct :revision revision))
259      (some #'(lambda(vc)
260                (end-revision-p vc revision))
261            (slot-p construct 'variants))))
262
263
264(defmethod changed-p ((construct TopicC) (revision integer))
265  "Returns t if the passed TopicC changed in the given version, i.e.
266   the <ReifiableConstructC>, <PersistentIdC>, <LocatorC>, <NameC>,
267   <OccurrenceC>, <AssociationC> or the reified-construct changed."
268  (or (call-next-method)
269      (some #'(lambda(vc)
270                (changed-p vc revision))
271            (union
272             (union
273              (union (psis construct :revision revision)
274                     (locators construct :revision revision))
275              (union (names construct :revision revision)
276                     (occurrences construct :revision revision)))
277             (delete-if-not
278              (lambda (assoc)
279                (eq (player (first (roles assoc :revision revision))
280                            :revision revision)
281                    construct))
282              (find-all-associations construct :revision revision))))
283      (let ((rc (reified-construct construct :revision revision)))
284        (when rc
285          (let ((ra (find-if #'(lambda(reifier-assoc)
286                                 (eql (reifiable-construct reifier-assoc) rc))
287                             (slot-p construct 'reified-construct))))
288            (changed-p ra revision))))
289      (some #'(lambda(vc)
290                (end-revision-p vc revision))
291            (union (union (union (slot-p construct 'psis)
292                                 (slot-p construct 'locators))
293                          (union (slot-p construct 'names)
294                                 (slot-p construct 'occurrences)))
295                   (slot-p construct 'reified-construct)))))
296           
297
298
299(defmethod changed-p ((construct AssociationC) (revision integer))
300  "Returns t if the passed AssociationC changed in the given version, i.e.
301   the <RoleC> or the <ReifiableConstructC> changed."
302  (or (call-next-method)
303      (some #'(lambda(role)
304                (changed-p role revision))
305            (roles construct :revision revision))
306      (some #'(lambda(vc)
307                (end-revision-p vc revision))
308            (slot-p construct 'roles))))
309
310
311(defpclass FragmentC ()
312  ((revision :type integer
313             :initarg :revision
314             :accessor revision
315             :index t
316             :documentation "revision in question")
317   (unique-id :initarg :unique-id
318              :accessor unique-id
319              :index t
320              :documentation "a unique id for this fragment. for now
321              just its OID, but may become a true UUID in the future")
322   (topic :type TopicC
323          :initarg :topic
324          :accessor topic
325          :index t
326          :documentation "changed topic (topicSI in Atom")
327   (serializer-cache :type String
328                     :initform nil
329                     :initarg :serializer-cache
330                     :documentation "contains te serialized string
331                                     value of this FragmentC instance,
332                                     that can contain any string format,
333                                     e.g. JTM, XTM, ... depending on the
334                                     setter method.")
335   (referenced-topics
336    :type list
337    :initarg :referenced-topics
338    :accessor referenced-topics
339    :documentation "list of topics that this topic references somehow (through associations, types, scopes in the characteristics etc.")
340   (associations
341    :type list
342    :initarg :associations
343    :accessor associations
344    :documentation "list of association that this topic is a player in")))
345
346(defmethod initialize-instance :after ((fragment FragmentC) &key)
347  "initialze the unique id of the fragment ot some suitable value"
348  (setf (slot-value fragment 'unique-id) (elephant::oid fragment)))
349
350
351(defun get-fragments (revision)
352  "Gets the list of all fragments for a given revision. Returns a
353list of FragmentC objects"
354  (declare (integer revision))
355  (let
356      ((cached-fragments
357        (elephant:get-instances-by-value 'FragmentC
358                                         'revision
359                                         revision)))
360    (if cached-fragments
361        cached-fragments
362        (remove 
363         nil 
364         (map
365          'list 
366          (lambda (top)
367            (when (changed-p top revision)
368              (make-instance 'FragmentC
369                             :revision revision
370                             :associations (find-associations
371                                            top :revision revision)
372                                        ;TODO: this quite probably introduces
373                                        ;code duplication with query: Check!
374                             :referenced-topics (find-referenced-topics
375                                                 top :revision revision)
376                             :topic top)))
377          (get-all-topics revision))))))
378
379(defun get-fragment (unique-id)
380  "get a fragment by its unique id"
381  (declare (integer unique-id))
382  (elephant:get-instance-by-value 'FragmentC 
383                                  'unique-id
384                                  unique-id))
385
386(defgeneric add-source-locator (construct &key source-locator revision)
387  (:documentation "adds an item identifier to a given construct based on the source
388                   locator and an internally generated id (ideally a uuid)"))
389
390
391(defmethod add-source-locator ((construct ReifiableConstructC) &key source-locator revision)
392  (declare (integer revision))
393  (unless
394      (some (lambda (ii)
395              (string-starts-with (uri ii) source-locator))
396            (item-identifiers construct :revision revision))
397    (let
398        ((ii-uri (format nil "~a/~d" source-locator (internal-id construct))))
399      (make-construct 'ItemIdentifierC
400                      :uri ii-uri
401                      :identified-construct construct
402                      :start-revision revision))))
403
404
405(defmethod add-source-locator ((top TopicC) &key source-locator revision)
406  ;topics already have the source locator in (at least) one PSI, so we
407  ;do not need to add an extra item identifier to them. However, we
408  ;need to do that for all their characteristics + associations
409  (mapc (lambda (name)
410          (add-source-locator name :revision revision
411                              :source-locator source-locator))
412        (names top :revision revision))
413  (mapc (lambda (occ)
414          (add-source-locator occ :revision revision
415                              :source-locator source-locator))
416        (occurrences top :revision revision))
417  (mapc (lambda (ass)
418          (add-source-locator ass :revision revision
419                              :source-locator source-locator))
420        (find-associations top :revision revision)))
421
422
423(defun create-latest-fragment-of-topic (topic-or-psi)
424  "Returns the latest fragment of the passed topic-psi"
425  (declare (type (or String TopicC) topic-or-psi))
426  (let ((topic (if (stringp topic-or-psi)
427                   (get-latest-topic-by-psi topic-or-psi)
428                   topic-or-psi)))
429    (when topic
430      (let ((start-revision
431             (start-revision
432              (find-if #'(lambda(x)
433                           (when (= 0 (end-revision x))
434                             t))
435                       (versions topic)))))
436        (let ((existing-fragment
437               (find-if #'(lambda(x)
438                            (when (eq topic (topic x))
439                              t))
440                        (get-fragments start-revision))))
441          (if existing-fragment
442              existing-fragment
443              (make-instance 'FragmentC
444                             :revision start-revision
445                             :associations (find-associations
446                                            topic :revision start-revision)
447                             :referenced-topics (find-referenced-topics
448                                                 topic :revision start-revision)
449                             :topic topic)))))))
450
451
452(defun get-latest-fragment-of-topic (topic-or-psi)
453  "Returns the latest existing fragment of the passed topic-psi."
454  (declare (type (or String TopicC) topic-or-psi))
455  (let ((topic (if (stringp topic-or-psi)
456                   (get-latest-topic-by-psi topic-or-psi)
457                   topic-or-psi)))
458    (when topic
459      (let ((existing-fragments
460             (elephant:get-instances-by-value 'FragmentC 'topic topic)))
461        (when existing-fragments
462          (first (sort existing-fragments
463                       #'(lambda(frg-1 frg-2)
464                           (> (revision frg-1) (revision frg-2))))))))))
465
466
467(defgeneric serializer-cache (fragment)
468  (:documentation "returns the slot value of serializer-cache or nil,
469                   if it is unbound.")
470  (:method ((fragment FragmentC))
471    (when (slot-boundp fragment 'serializer-cache)
472      (slot-value fragment 'serializer-cache))))
473
474
475(defgeneric serialize-fragment (fragment serializer)
476  (:documentation "returns a string that represents the serialization
477                   of the passed fragment instance.
478                   This method uses the fragments serializer-cache
479                   slot to perform faster, i.e. if the fragment was
480                   once serialized, the next time the cached serialized
481                   data is used again.")
482  (:method ((fragment FragmentC) (serializer Function))
483    (cond ((null (serializer-cache fragment))
484           (setf (slot-value fragment 'serializer-cache)
485                 (funcall serializer fragment)))
486          (t
487           (serializer-cache fragment)))))
488           
489         
490                 
Note: See TracBrowser for help on using the repository browser.