source: branches/gdl-frontend/src/model/changes.lisp

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

jtm-importer + gdl-interface: added a boolean variable to the jtm-importer, so a fragment of each topic that is contained in the received jtm-fragment can be created automiticaly

  • Property svn:eol-style set to native
File size: 16.9 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    (remove-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      (remove-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     (remove-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             (remove-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   (referenced-topics
328    :type list
329    :initarg :referenced-topics
330    :accessor referenced-topics
331    :documentation "list of topics that this topic references somehow (through associations, types, scopes in the characteristics etc.")
332   (associations
333    :type list
334    :initarg :associations
335    :accessor associations
336    :documentation "list of association that this topic is a player in")))
337
338(defmethod initialize-instance :after ((fragment FragmentC) &key)
339  "initialze the unique id of the fragment ot some suitable value"
340  (setf (slot-value fragment 'unique-id) (elephant::oid fragment)))
341
342
343(defun get-fragments (revision)
344  "Gets the list of all fragments for a given revision. Returns a
345list of FragmentC objects"
346  (declare (integer revision))
347  (let
348      ((cached-fragments
349        (elephant:get-instances-by-value 'FragmentC
350                                         'revision
351                                         revision)))
352    (if cached-fragments
353        cached-fragments
354        (remove 
355         nil 
356         (map
357          'list 
358          (lambda (top)
359            (when (changed-p top revision)
360              (make-instance 'FragmentC
361                             :revision revision
362                             :associations (find-associations
363                                            top :revision revision)
364                                        ;TODO: this quite probably introduces
365                                        ;code duplication with query: Check!
366                             :referenced-topics (find-referenced-topics
367                                                 top :revision revision)
368                             :topic top)))
369          (get-all-topics revision))))))
370
371(defun get-fragment (unique-id)
372  "get a fragment by its unique id"
373  (declare (integer unique-id))
374  (elephant:get-instance-by-value 'FragmentC 
375                                  'unique-id
376                                  unique-id))
377
378(defgeneric add-source-locator (construct &key source-locator revision)
379  (:documentation "adds an item identifier to a given construct based on the source
380                   locator and an internally generated id (ideally a uuid)"))
381
382
383(defmethod add-source-locator ((construct ReifiableConstructC) &key source-locator revision)
384  (declare (integer revision))
385  (unless
386      (some (lambda (ii)
387              (string-starts-with (uri ii) source-locator))
388            (item-identifiers construct :revision revision))
389    (let
390        ((ii-uri (format nil "~a/~d" source-locator (internal-id construct))))
391      (make-construct 'ItemIdentifierC
392                      :uri ii-uri
393                      :identified-construct construct
394                      :start-revision revision))))
395
396
397(defmethod add-source-locator ((top TopicC) &key source-locator revision)
398  ;topics already have the source locator in (at least) one PSI, so we
399  ;do not need to add an extra item identifier to them. However, we
400  ;need to do that for all their characteristics + associations
401  (mapc (lambda (name)
402          (add-source-locator name :revision revision
403                              :source-locator source-locator))
404        (names top :revision revision))
405  (mapc (lambda (occ)
406          (add-source-locator occ :revision revision
407                              :source-locator source-locator))
408        (occurrences top :revision revision))
409  (mapc (lambda (ass)
410          (add-source-locator ass :revision revision
411                              :source-locator source-locator))
412        (find-associations top :revision revision)))
413
414
415(defun create-latest-fragment-of-topic (topic-or-psi)
416  "Returns the latest fragment of the passed topic-psi"
417  (declare (type (or String TopicC) topic-or-psi))
418  (let ((topic (if (stringp topic-or-psi)
419                   (get-latest-topic-by-psi topic-or-psi)
420                   topic-or-psi)))
421    (when topic
422      (let ((start-revision
423             (start-revision
424              (find-if #'(lambda(x)
425                           (when (= 0 (end-revision x))
426                             t))
427                       (versions topic)))))
428        (let ((existing-fragment
429               (find-if #'(lambda(x)
430                            (when (eq topic (topic x))
431                              t))
432                        (get-fragments start-revision))))
433          (if existing-fragment
434              existing-fragment
435              (make-instance 'FragmentC
436                             :revision start-revision
437                             :associations (find-associations
438                                            topic :revision start-revision)
439                             :referenced-topics (find-referenced-topics
440                                                 topic :revision start-revision)
441                             :topic topic)))))))
442
443
444(defun get-latest-fragment-of-topic (topic-psi)
445  "Returns the latest existing fragment of the passed topic-psi."
446  (declare (string topic-psi))
447  (let ((topic (get-latest-topic-by-psi topic-psi)))
448    (when topic
449      (let ((existing-fragments
450             (elephant:get-instances-by-value 'FragmentC 'topic topic)))
451        (when existing-fragments
452          (first (sort existing-fragments
453                       #'(lambda(frg-1 frg-2)
454                           (> (revision frg-1) (revision frg-2))))))))))
Note: See TracBrowser for help on using the repository browser.