Changeset 325 for trunk/src/model
- Timestamp:
- 10/10/10 09:41:19 (15 years ago)
- Location:
- trunk/src/model
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified trunk/src/model/changes.lisp ¶
r229 r325 8 8 9 9 10 ;-*- standard-indent:2; tab-width:2; indent-tabs-mode:nil -*-11 10 (in-package :datamodel) 12 11 13 12 (defun get-all-revisions () 14 13 "Returns an ordered set of the start dates of all revisions in the engine" 15 ;TODO: this is a very inefficient implementation... it would equally 16 ;be possible to have a separate object that stored all such 17 ;revisions and only make the search from the latest version that's 18 ;stored their 19 (let 20 ((revision-set)) 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)) 21 19 (dolist (vi (elephant:get-instances-by-class 'VersionInfoC)) 22 20 (pushnew (start-revision vi) revision-set)) 23 21 (sort revision-set #'<))) 22 24 23 25 24 (defun get-all-revisions-for-tm (tm-id) … … 30 29 (tops-and-assocs (when tm (union (topics tm) (associations tm)))) 31 30 (revision-set nil)) 32 ;(format t "tops-and-assocs: ~a~&" (mapcan #'versions tops-and-assocs))33 31 (dolist (vi (mapcan #'versions tops-and-assocs)) 34 ;(format t "(start-revision vi): ~a~&" (start-revision vi))35 32 (pushnew (start-revision vi) revision-set)) 36 33 (sort revision-set #'<))) 37 34 38 35 39 (defun find-associations-for-topic (top) 40 "find all associations of this topic" 41 (let 42 ((type-instance-topic 43 (d:identified-construct 44 (elephant:get-instance-by-value 'PersistentIdC 45 'uri 46 "http://psi.topicmaps.org/iso13250/model/type-instance")))) 47 (remove 48 type-instance-topic 49 (remove-duplicates 50 (map 'list #'parent (player-in-roles top))) 51 :key #'instance-of))) 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-duplicates 41 (map 'list #'(lambda(role) 42 (parent role :revision revision)) 43 (player-in-roles instance :revision revision))))) 44 45 46 (defgeneric find-associations (instance &key revision) 47 (:documentation "Finds all associations of this topic except 48 type-instance-associations.") 49 (:method ((instance TopicC) &key (revision *TM-REVISION*)) 50 (declare (type (or integer null) revision)) 51 (let ((type-instance-topic 52 (d:identified-construct 53 (elephant:get-instance-by-value 54 'PersistentIdC 'uri *type-instance-psi*)))) 55 (remove-if 56 #'(lambda(assoc) 57 (eql (instance-of assoc :revision revision) 58 type-instance-topic)) 59 (find-all-associations instance :revision revision))))) 52 60 53 61 54 (defgeneric find-referenced-topics (construct )62 (defgeneric find-referenced-topics (construct &key revision) 55 63 (:documentation "find all the topics that are references from this construct as type, scope or player, as the case may be")) 56 64 57 (defmethod find-referenced-topics ((characteristic CharacteristicC)) 58 "characteristics are scopable + typable" 65 66 (defmethod find-referenced-topics ((characteristic CharacteristicC) 67 &key (revision *TM-REVISION*)) 68 "characteristics are scopable + typable + reifiable" 59 69 (append 60 (when (reifier characteristic) 61 (list (reifier characteristic))) 62 (themes characteristic) 63 (when (instance-of-p characteristic) 64 (list (instance-of characteristic))) 70 (when (reifier characteristic :revision revision) 71 (list (reifier characteristic :revision revision))) 72 (themes characteristic :revision revision) 73 (when (instance-of characteristic :revision revision) 74 (list (instance-of characteristic :revision revision))) 75 (when (and (typep characteristic 'NameC) 76 (variants characteristic :revision revision)) 77 (remove-if #'null 78 (loop for var in (variants characteristic :revision revision) 79 append (find-referenced-topics var :revision revision)))) 65 80 (when (and (typep characteristic 'OccurrenceC) 66 81 (> (length (charvalue characteristic)) 0) 67 82 (eq #\# (elt (charvalue characteristic) 0))) 68 (list (get-item-by-id (subseq (charvalue characteristic) 1)))))) 69 70 71 (defmethod find-referenced-topics ((role RoleC)) 83 (list (get-item-by-id (subseq (charvalue characteristic) 1) 84 :revision revision))))) 85 86 87 (defmethod find-referenced-topics ((role RoleC) 88 &key (revision *TM-REVISION*)) 72 89 (append 73 (when (reifier role) 74 (list (reifier role))) 75 (list (instance-of role)) 76 (list (player role)))) 77 78 (defmethod find-referenced-topics ((association AssociationC)) 90 (when (reifier role :revision revision) 91 (list (reifier role :revision revision))) 92 (list (instance-of role :revision revision)) 93 (list (player role :revision revision)))) 94 95 96 (defmethod find-referenced-topics ((association AssociationC) 97 &key (revision *TM-REVISION*)) 79 98 "associations are scopable + typable" 80 99 (append 81 (when (reifier association) 82 (list (reifier association))) 83 (list (instance-of association)) 84 (themes association) 85 (mapcan #'find-referenced-topics (roles association)))) 100 (when (reifier association :revision revision) 101 (list (reifier association :revision revision))) 102 (list (instance-of association :revision revision)) 103 (themes association :revision revision) 104 (mapcan #'(lambda(role) 105 (find-referenced-topics role :revision revision)) 106 (roles association :revision revision)))) 86 107 87 108 88 (defmethod find-referenced-topics ((top TopicC)) 109 (defmethod find-referenced-topics ((top TopicC) 110 &key (revision *TM-REVISION*)) 89 111 "Part 1b of the eGov-Share spec states: 90 112 # for each topicname in T export a topic stub for each scope topic … … 99 121 top 100 122 (append 101 (list-instanceOf top) 102 (mapcan #'find-referenced-topics (names top)) 103 (mapcan #'find-referenced-topics (mapcan #'variants (names top))) 104 (mapcan #'find-referenced-topics (occurrences top)) 105 (mapcan #'find-referenced-topics (find-associations-for-topic top)))))) 123 (list-instanceOf top :revision revision) 124 (mapcan #'(lambda(name) 125 (find-referenced-topics name :revision revision)) 126 (names top :revision revision)) 127 (mapcan #'(lambda(variant) 128 (find-referenced-topics variant :revision revision)) 129 (mapcan #'variants (names top :revision revision))) 130 (mapcan #'(lambda(occ) 131 (find-referenced-topics occ :revision revision)) 132 (occurrences top :revision revision)) 133 (mapcan #'(lambda(assoc) 134 (find-referenced-topics assoc :revision revision)) 135 (find-associations top :revision revision)))))) 106 136 107 137 138 (defgeneric initial-version-p (version-info) 139 (:documentation "A helper function for changed-p that returns the passed 140 version-info object if it is the initial version-info object, 141 i.e. it owns the smallest start-revsion of the 142 version-construct.") 143 (:method ((version-info VersionInfoC)) 144 (unless (find-if #'(lambda(vi) 145 (< (start-revision vi) (start-revision version-info))) 146 (versions (versioned-construct version-info))) 147 version-info))) 148 149 108 150 (defgeneric changed-p (construct revision) 109 (:documentation "Has the topic map construct changed in a given revision? 'Changed' can mean: 151 (:documentation "Has the topic map construct changed in a given revision? 152 'Changed' can mean: 110 153 * newly created 154 * deletion of an element 111 155 * modified through the addition or removal of identifiers 112 * (for associations) modified through the addition or removal of identifiers in the association or one of its roles 113 * (for topics) modified through the addition or removal of identifiers or characteristics 114 * (for topics) modified through the addition or removal of an association in which it is first player")) 156 * (for associations) modified through the addition or removal of 157 identifiers in the association or one of its roles 158 * (for topics) modified through the addition or removal of identifiers 159 or characteristics 160 * (for topics) modified through the addition or removal of an association 161 in which it is first player")) 162 115 163 116 164 (defmethod changed-p ((construct TopicMapConstructC) (revision integer)) 117 "The 'normal' case: changes only when new identifiers are added" 118 (find revision (versions construct) :test #'= :key #'start-revision)) 119 120 ;There is quite deliberately no method specialized on AssociationC as 121 ;copy-item-identifiers for Associations already guarantees that the 122 ;version history of an association is only updated when the 123 ;association itself is really updated 124 125 (defmethod changed-p ((topic TopicC) (revision integer)) 126 "A topic is changed if one of its child elements (identifiers or 127 characteristics) or one of the associations in which it is first player has changed" 128 (let* 129 ((first-player-in-associations 130 (remove-if-not 131 (lambda (association) 132 (eq (player (first (roles association))) 133 topic)) 134 (find-associations-for-topic topic))) 135 (all-constructs 136 (union 137 (get-all-identifiers-of-construct topic) 138 (union 139 (names topic) 140 (union 141 (occurrences topic) 142 first-player-in-associations))))) 143 (some 144 (lambda (construct) 145 (changed-p construct revision)) 146 all-constructs))) 165 "changed-p returns nil for TopicMapConstructCs that are not specified 166 more detailed. The actual algorithm is processed for all 167 VersionedConstructCs." 168 (declare (ignorable revision)) 169 nil) 170 171 172 (defmethod changed-p ((construct PointerC) (revision integer)) 173 "Returns t if the PointerC was added to a construct the first 174 time in the passed revision" 175 (let ((version-info (some #'(lambda(pointer-association) 176 (changed-p pointer-association revision)) 177 (slot-p construct 'identified-construct)))) 178 (when version-info 179 (initial-version-p version-info)))) 180 181 182 (defmethod changed-p ((construct VersionedConstructC) (revision integer)) 183 "changed-p returns t if there exist a VersionInfoC with the given start-revision." 184 (let ((version-info 185 (find revision (versions construct) :test #'= :key #'start-revision))) 186 (when version-info 187 (initial-version-p version-info)))) 188 189 190 (defmethod changed-p ((construct CharacteristicC) (revision integer)) 191 "Returns t if the CharacteristicC was added to a construct in the passed 192 revision or if <ReifiableConstructC> changed." 193 (or (call-next-method) 194 (let ((version-info 195 (some #'(lambda(characteristic-association) 196 (changed-p characteristic-association revision)) 197 (slot-p construct 'parent)))) 198 (when version-info 199 (initial-version-p version-info))))) 200 201 202 (defmethod changed-p ((construct RoleC) (revision integer)) 203 "Returns t if the RoleC was added to a construct in the passed 204 revision or if <ReifiableConstructC> changed." 205 (or (call-next-method) 206 (let ((version-info 207 (some #'(lambda(role-association) 208 (changed-p role-association revision)) 209 (slot-p construct 'parent)))) 210 (when version-info 211 (initial-version-p version-info))))) 212 213 214 (defgeneric end-revision-p (construct revision) 215 (:documentation "A helper function for changed-p. It returns the latest 216 version-info if the passed versioned-construct was 217 marked-as-deleted in the version that is given.") 218 (:method ((construct VersionedConstructC) (revision integer)) 219 (let ((version-info (find revision (versions construct) 220 :key #'end-revision :test #'=))) 221 (when (and version-info 222 (not 223 (find-if 224 #'(lambda(vi) 225 (or (> (end-revision vi) (end-revision version-info)) 226 (= (end-revision vi) 0))) 227 (versions construct)))) 228 version-info)))) 229 230 231 (defmethod changed-p ((construct ReifiableConstructC) (revision integer)) 232 "Returns t if a ReifiableConstructC changed in the given version, i.e. 233 an item-identifier or reifier was added to the construct itself." 234 (or (some #'(lambda(vc) 235 (changed-p vc revision)) 236 (union (item-identifiers construct :revision revision) 237 (let ((reifier-top (reifier construct :revision revision))) 238 (when reifier-top 239 (list reifier-top))))) 240 (some #'(lambda(vc) 241 (end-revision-p vc revision)) 242 (union (slot-p construct 'item-identifiers) 243 (slot-p construct 'reifier))))) 244 245 246 (defmethod changed-p ((construct NameC) (revision integer)) 247 "Returns t if the passed NameC changed in the given version, i.e. 248 the <ReifiableConstructC> characteristics or the variants changed." 249 (or (call-next-method) 250 (some #'(lambda(var) 251 (changed-p var revision)) 252 (variants construct :revision revision)) 253 (some #'(lambda(vc) 254 (end-revision-p vc revision)) 255 (slot-p construct 'variants)))) 256 257 258 (defmethod changed-p ((construct TopicC) (revision integer)) 259 "Returns t if the passed TopicC changed in the given version, i.e. 260 the <ReifiableConstructC>, <PersistentIdC>, <LocatorC>, <NameC>, 261 <OccurrenceC>, <AssociationC> or the reified-construct changed." 262 (or (call-next-method) 263 (some #'(lambda(vc) 264 (changed-p vc revision)) 265 (union 266 (union 267 (union (psis construct :revision revision) 268 (locators construct :revision revision)) 269 (union (names construct :revision revision) 270 (occurrences construct :revision revision))) 271 (remove-if-not 272 (lambda (assoc) 273 (eq (player (first (roles assoc :revision revision)) 274 :revision revision) 275 construct)) 276 (find-all-associations construct :revision revision)))) 277 (let ((rc (reified-construct construct :revision revision))) 278 (when rc 279 (let ((ra (find-if #'(lambda(reifier-assoc) 280 (eql (reifiable-construct reifier-assoc) rc)) 281 (slot-p construct 'reified-construct)))) 282 (changed-p ra revision)))) 283 (some #'(lambda(vc) 284 (end-revision-p vc revision)) 285 (union (union (union (slot-p construct 'psis) 286 (slot-p construct 'locators)) 287 (union (slot-p construct 'names) 288 (slot-p construct 'occurrences))) 289 (slot-p construct 'reified-construct))))) 290 291 292 293 (defmethod changed-p ((construct AssociationC) (revision integer)) 294 "Returns t if the passed AssociationC changed in the given version, i.e. 295 the <RoleC> or the <ReifiableConstructC> changed." 296 (or (call-next-method) 297 (some #'(lambda(role) 298 (changed-p role revision)) 299 (roles construct :revision revision)) 300 (some #'(lambda(vc) 301 (end-revision-p vc revision)) 302 (slot-p construct 'roles)))) 147 303 148 304 … … 192 348 (remove 193 349 nil 194 (map 'list 195 (lambda (top) 196 (when (changed-p top revision) 197 (make-instance 'FragmentC 198 :revision revision 199 :associations (find-associations-for-topic top) ;TODO: this quite probably introduces code duplication with query: Check! 200 :referenced-topics (find-referenced-topics top) 201 :topic top))) 202 (elephant:get-instances-by-class 'TopicC)))))) 350 (map 351 'list 352 (lambda (top) 353 (when (changed-p top revision) 354 (make-instance 'FragmentC 355 :revision revision 356 :associations (find-associations 357 top :revision revision) 358 ;TODO: this quite probably introduces 359 ;code duplication with query: Check! 360 :referenced-topics (find-referenced-topics 361 top :revision revision) 362 :topic top))) 363 (get-all-topics revision)))))) 203 364 204 365 (defun get-fragment (unique-id) … … 209 370 unique-id)) 210 371 211 (defgeneric mark-as-deleted (construct &key source-locator revision)212 (:documentation "Mark a construct as deleted if it comes from the source indicated by213 source-locator"))214 215 (defmethod mark-as-deleted ((construct TopicMapConstructC) &key source-locator revision)216 "Mark a topic as deleted if it comes from the source indicated by217 source-locator"218 (declare (ignorable source-locator))219 (let220 ((last-version ;the last active version221 (find 0 (versions construct) :key #'end-revision)))222 (when last-version223 (setf (end-revision last-version) revision))))224 225 (defmethod mark-as-deleted :around ((ass AssociationC) &key source-locator revision)226 "Mark an association and its roles as deleted"227 (mapc (lambda (role) (mark-as-deleted role :revision revision :source-locator source-locator))228 (roles ass))229 (call-next-method))230 231 (defmethod mark-as-deleted :around ((top TopicC) &key source-locator revision)232 "Mark a topic as deleted if it comes from the source indicated by233 source-locator"234 ;;Part 1b, 1.4.3.3.1:235 ;; Let SP be the value of the ServerSourceLocatorPrefix element in the ATOM feed F236 ;; * Let SI be the value of TopicSI element in ATOM entry E237 ;; * feed F contains E238 ;; * entry E references topic fragment TF239 ;; * Let LTM be the local topic map240 ;; * Let T be the topic in LTM that has a subjectidentifier that matches SI241 ;; * For all names, occurrences and associations in which T plays a role, TMC242 ;; * Delete all SrcLocators of TMC that begin with SP. If the count of srclocators on TMC = 0 then delete TMC243 ;; * Merge in the fragment TF using SP as the base all generated source locators.244 245 (when246 (some (lambda (psi) (string-starts-with (uri psi) source-locator)) (psis top))247 (mapc (lambda (name) (mark-as-deleted name :revision revision :source-locator source-locator))248 (names top))249 (mapc (lambda (occ) (mark-as-deleted occ :revision revision :source-locator source-locator))250 (occurrences top))251 (mapc (lambda (ass) (mark-as-deleted ass :revision revision :source-locator source-locator))252 (find-associations-for-topic top))253 (call-next-method)))254 255 372 (defgeneric add-source-locator (construct &key source-locator revision) 256 373 (:documentation "adds an item identifier to a given construct based on the source 257 locator and an internally generated id (ideally a uuid)")) 374 locator and an internally generated id (ideally a uuid)")) 375 258 376 259 377 (defmethod add-source-locator ((construct ReifiableConstructC) &key source-locator revision) 260 (declare (i gnorablerevision))378 (declare (integer revision)) 261 379 (unless 262 (some (lambda (ii) (string-starts-with (uri ii) source-locator)) (item-identifiers construct)) 380 (some (lambda (ii) 381 (string-starts-with (uri ii) source-locator)) 382 (item-identifiers construct :revision revision)) 263 383 (let 264 384 ((ii-uri (format nil "~a/~d" source-locator (internal-id construct)))) 265 (make-instance 'ItemIdentifierC :uri ii-uri :identified-construct construct :start-revision revision)))) 385 (make-construct 'ItemIdentifierC 386 :uri ii-uri 387 :identified-construct construct 388 :start-revision revision)))) 389 266 390 267 391 (defmethod add-source-locator ((top TopicC) &key source-locator revision) … … 269 393 ;do not need to add an extra item identifier to them. However, we 270 394 ;need to do that for all their characteristics + associations 271 (mapc (lambda (name) (add-source-locator name :revision revision :source-locator source-locator)) 272 (names top)) 273 (mapc (lambda (occ) (add-source-locator occ :revision revision :source-locator source-locator)) 274 (occurrences top)) 275 (mapc (lambda (ass) (add-source-locator ass :revision revision :source-locator source-locator)) 276 (find-associations-for-topic top))) 395 (mapc (lambda (name) 396 (add-source-locator name :revision revision 397 :source-locator source-locator)) 398 (names top :revision revision)) 399 (mapc (lambda (occ) 400 (add-source-locator occ :revision revision 401 :source-locator source-locator)) 402 (occurrences top :revision revision)) 403 (mapc (lambda (ass) 404 (add-source-locator ass :revision revision 405 :source-locator source-locator)) 406 (find-associations top :revision revision))) 277 407 278 408 … … 280 410 "Returns the latest fragment of the passed topic-psi" 281 411 (declare (string topic-psi)) 282 (let ((topic 283 (get-item-by-psi topic-psi))) 412 (let ((topic (get-latest-topic-by-psi topic-psi))) 284 413 (when topic 285 414 (let ((start-revision … … 298 427 (make-instance 'FragmentC 299 428 :revision start-revision 300 :associations (find-associations-for-topic topic) 301 :referenced-topics (find-referenced-topics topic) 429 :associations (find-associations 430 topic :revision start-revision) 431 :referenced-topics (find-referenced-topics 432 topic :revision start-revision) 302 433 :topic topic))))))) 303 434 … … 306 437 "Returns the latest existing fragment of the passed topic-psi." 307 438 (declare (string topic-psi)) 308 (let ((topic 309 (get-item-by-psi topic-psi))) 439 (let ((topic (get-latest-topic-by-psi topic-psi))) 310 440 (when topic 311 441 (let ((existing-fragments -
TabularUnified trunk/src/model/datamodel.lisp ¶
r284 r325 8 8 9 9 10 ;-*- standard-indent: 2; indent-tabs-mode: nil -*-11 10 (defpackage :datamodel 12 11 (:use :cl :elephant :constants) 13 12 (:nicknames :d) 14 13 (:import-from :exceptions 15 missing-reference-error 16 no-identifier-error 17 duplicate-identifier-error 18 object-not-found-error) 19 (:export :AssociationC ;; types 20 :CharacteristicC 21 :FragmentC 22 :IdentifierC 23 :IdentityC 24 :ItemIdentifierC 25 :NameC 14 duplicate-identifier-error 15 object-not-found-error 16 missing-argument-error 17 not-mergable-error 18 tm-reference-error) 19 (:import-from :constants 20 *xml-string* 21 *instance-psi*) 22 (:export ;;classes 23 :TopicMapConstructC 24 :VersionedConstructC 25 :ReifiableConstructC 26 :ScopableC 27 :TypableC 28 :TopicMapC 29 :AssociationC 30 :RoleC 31 :CharacteristicC 26 32 :OccurrenceC 33 :NameC 34 :VariantC 35 :PointerC 36 :IdentifierC 27 37 :PersistentIdC 28 :ReifiableConstructC 29 :RoleC 30 :ScopableC 31 :SubjectLocatorC 32 :TopicC 33 :TopicIdentificationC 34 :TopicMapC 35 :TopicMapConstructC 36 :TypableC 37 :VariantC 38 39 ;; functions and slot accessors 40 :in-topicmaps 41 :add-to-topicmap 42 :add-source-locator 43 :associations 44 :changed-p 45 :charvalue 46 :check-for-duplicate-identifiers 47 :datatype 48 :equivalent-constructs 49 :find-item-by-revision 50 :find-most-recent-revision 51 :get-all-revisions 52 :get-all-revisions-for-tm 53 :get-fragment 54 :get-fragments 55 :get-revision 56 :get-item-by-content 57 :get-item-by-id 58 :get-item-by-item-identifier 59 :get-item-by-psi 60 :identified-construct 61 :identified-construct-p 62 :in-topicmap 63 :internal-id 64 :instance-of 65 :instance-of-p 66 :item-identifiers 67 :item-identifiers-p 68 :list-instanceOf 69 :list-super-types 70 :locators 71 :locators-p 72 :make-construct 73 :mark-as-deleted 74 :names 75 :namevalue 76 :occurrences 77 :name 78 :parent 79 :player 80 :player-in-roles 81 :players 82 :psis 83 :psis-p 84 :referenced-topics 85 :revision 86 :RoleC-p 87 :roleid 88 :roles 89 :themes 90 :xtm-id 91 :xtm-id-p 92 :topic 93 :topicid 94 :topic-identifiers 95 :topics 96 :unique-id 97 :uri 98 :uri-p 38 :ItemIdentifierC 39 :SubjectLocatorC 40 :TopicIdentificationC 41 :TopicC 42 :FragmentC 43 44 ;;methods, functions and macros 45 :xtm-id 46 :uri 47 :identified-construct 48 :item-identifiers 49 :add-item-identifier 50 :delete-item-identifier 51 :reifier 52 :add-reifier 53 :delete-reifier 54 :find-item-by-revision 55 :find-most-recent-revision 56 :themes 57 :add-theme 58 :delete-theme 59 :instance-of 60 :add-type 61 :delete-type 62 :parent 63 :add-parent 64 :delete-parent 65 :variants 66 :add-variant 67 :delete-variant 68 :player 69 :add-player 70 :delete-player 71 :roles 72 :add-role 73 :delete-role 74 :associations 75 :topics 76 :add-to-tm 77 :delete-from-tm 78 :psis 79 :add-psi 80 :delete-psi 81 :topic-identifiers 82 :add-topic-identifier 83 :delete-topic-identifier 84 :topic-id 85 :locators 86 :add-locator 87 :delete-locator 88 :names 89 :add-name 90 :delete-name 91 :occurrences 92 :add-occurrence 93 :delete-occurrence 94 :player-in-roles 99 95 :used-as-type 100 96 :used-as-theme 101 :variants 102 :xor 103 :create-latest-fragment-of-topic 97 :datatype 98 :charvalue 99 :reified-construct 100 :mark-as-deleted 101 :marked-as-deleted-p 102 :in-topicmaps 103 :delete-construct 104 :get-revision 105 :get-item-by-id 106 :get-item-by-psi 107 :get-item-by-item-identifier 108 :get-item-by-locator 109 :get-item-by-content 110 :string-integer-p 111 :with-revision 104 112 :get-latest-fragment-of-topic 105 :reified 106 :reifier 107 :add-reifier 108 :remove-reifier 109 110 :*current-xtm* ;; special variables 111 :*TM-REVISION* 112 113 :with-revision ;;macros 114 115 :string-starts-with ;;helpers 116 )) 117 118 (declaim (optimize (debug 3) (safety 3) (speed 0) (space 0))) 113 :create-latest-fragment-of-topic 114 :PointerC-p 115 :IdentifierC-p 116 :SubjectLocatorC-p 117 :PersistentIdC-p 118 :ItemIdentifierC-p 119 :TopicIdentificationC-p 120 :CharacteristicC-p 121 :OccurrenceC-p 122 :NameC-p 123 :VariantC-p 124 :ScopableC-p 125 :TypableC-p 126 :TopicC-p 127 :AssociationC-p 128 :RoleC-p 129 :TopicMapC-p 130 :ReifiableConstructC-p 131 :TopicMapConstructC-p 132 :VersionedConstructC-p 133 :make-construct 134 :list-instanceOf 135 :list-super-types 136 :in-topicmap 137 :string-starts-with 138 :get-fragments 139 :get-fragment 140 :get-all-revisions 141 :unique-id 142 :topic 143 :referenced-topics 144 :revision 145 :get-all-revisions-for-tm 146 :add-source-locator 147 :changed-p 148 :check-for-duplicate-identifiers 149 :find-item-by-content 150 :rec-remf 151 :get-all-topics 152 :get-all-associations 153 :get-all-tms 154 155 156 ;;globals 157 :*TM-REVISION* 158 :*CURRENT-XTM*)) 159 119 160 (in-package :datamodel) 120 161 121 (defparameter *current-xtm* nil "Represents the currently active TM") 122 123 (defmacro find-max-elem (candidate-list &key (relop #'> relop-p) (key #'identity key-p)) 124 "Given a non-empty list, return the maximum element in the list. 125 If provided, then relop must be a relational operator that determines the ordering; 126 else #'> is used. The keyword parameter key may name a function that is used to extract 127 the sort key; otherwise the elements themselves are the sort keys." 128 (let 129 ((candidate-list-value-name (gensym)) 130 (relop-value-name (gensym)) 131 (key-value-name (gensym)) 132 (best-seen-cand-name (gensym)) 133 (max-key-name (gensym)) 134 (inspected-cand-name (gensym)) 135 (inspected-key-name (gensym))) 136 (let 137 ((max-key-init (if key-p 138 `(funcall ,key-value-name ,best-seen-cand-name) 139 best-seen-cand-name)) 140 (inspected-key-init (if key-p 141 `(funcall ,key-value-name ,inspected-cand-name) 142 inspected-cand-name)) 143 (relexp (if relop-p 144 `(funcall ,relop-value-name ,inspected-key-name ,max-key-name) 145 `(> ,inspected-key-name ,max-key-name)))) 146 (let 147 ((initializers `((,candidate-list-value-name ,candidate-list) 148 (,best-seen-cand-name (first ,candidate-list-value-name)) 149 (,max-key-name ,max-key-init)))) 150 (when relop-p 151 (push `(,relop-value-name ,relop) initializers)) 152 (when key-p 153 (push `(,key-value-name ,key) initializers)) 154 `(let* 155 ,initializers 156 (dolist (,inspected-cand-name (rest ,candidate-list-value-name)) 157 (let 158 ((,inspected-key-name ,inspected-key-init)) 159 (when ,relexp 160 (setf ,best-seen-cand-name ,inspected-cand-name) 161 (setf ,max-key-name ,inspected-key-name)))) 162 ,best-seen-cand-name))))) 163 162 163 ;;TODO: implement a macro with-merge-constructs, that merges constructs 164 ;; after all operations in the body were called 165 166 167 168 ;;; globals ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 164 169 (defvar *TM-REVISION* 0) 170 171 172 (defparameter *CURRENT-XTM* nil "Represents the currently active TM.") 173 174 175 ;;; classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 176 ;;; versioning 177 (defpclass VersionInfoC() 178 ((start-revision :initarg :start-revision 179 :accessor start-revision 180 :type integer 181 :initform 0 182 :documentation "The start-revision of the version's 183 interval of a versioned object.") 184 (end-revision :initarg :end-revision 185 :accessor end-revision 186 :type integer 187 :initform 0 188 :documentation "The end-revision of the version's interval 189 of a versioned object.") 190 (versioned-construct :initarg :versioned-construct 191 :accessor versioned-construct 192 :associate VersionedConstructC 193 :documentation "The reference of the versioned 194 object that is described by this 195 VersionInfoC-object.")) 196 (:documentation "A VersionInfoC-object describes the revision information 197 of a versioned object in intervals starting by the value 198 start-revision and ending by the value end-revision - 1. 199 end-revision=0 means always the latest version.")) 200 201 202 (defpclass VersionedConstructC() 203 ((versions :initarg :versions 204 :accessor versions 205 :inherit t 206 :associate (VersionInfoC versioned-construct) 207 :documentation "Version infos for former versions of this base 208 class."))) 209 210 211 ;;; base classes ... 212 (defpclass TopicMapConstructC() 213 () 214 (:documentation "An abstract base class for all classes that describes 215 Topic Maps data.")) 216 217 218 (defpclass ScopableC() 219 ((themes :associate (ScopeAssociationC scopable-construct) 220 :inherit t 221 :documentation "Contains all association-objects that contain the 222 actual scope-topics.")) 223 (:documentation "An abstract base class for all constructs that are scoped.")) 224 225 226 (defpclass TypableC() 227 ((instance-of :associate (TypeAssociationC typable-construct) 228 :inherit t 229 :documentation "Contains all association-objects that contain 230 the actual type-topic.")) 231 (:documentation "An abstract base class for all typed constructcs.")) 232 233 234 (defpclass DatatypableC() 235 ((datatype :accessor datatype 236 :initarg :datatype 237 :initform constants:*xml-string* 238 :type string 239 :index t 240 :documentation "The XML Schema datatype of the occurrencevalue 241 (optional, always IRI for resourceRef).")) 242 (:documentation "An abstract base class for characteristics that own 243 an xml-datatype.")) 244 245 246 ;;; pointers ... 247 (defpclass PointerC(TopicMapConstructC) 248 ((uri :initarg :uri 249 :accessor uri 250 :inherit t 251 :type string 252 :initform (error (make-missing-argument-condition "From PointerC(): uri must be set for a pointer" 'uri ':uri)) 253 :index t 254 :documentation "The actual value of a pointer, i.e. uri or ID.") 255 (identified-construct :associate (PointerAssociationC identifier) 256 :inherit t 257 :documentation "Associates a association-object that 258 additionally stores some 259 version-infos.")) 260 (:documentation "An abstract base class for all pointers.")) 261 262 263 (defpclass IdentifierC(PointerC) 264 () 265 (:documentation "An abstract base class for all TM-Identifiers.")) 266 267 268 (defpclass TopicIdentificationC(PointerC) 269 ((xtm-id :initarg :xtm-id 270 :accessor xtm-id 271 :type string 272 :initform (error (make-missing-argument-condition "From TopicIdentificationC(): xtm-id must be seet for a topic-identifier" 'xtm-id ':xtm-id)) 273 :index t 274 :documentation "ID of the TM this identification came from.")) 275 (:index t) 276 (:documentation "Identify topic items through generalized topic-ids. 277 A topic may have many original topicids, the class 278 representing one of them.")) 279 280 281 (defpclass SubjectLocatorC(IdentifierC) 282 () 283 (:index t) 284 (:documentation "A subject-locator that contains an uri-value and an 285 association to SubjectLocatorAssociationC's which are in 286 turn associated with TopicC's.")) 287 288 289 (defpclass PersistentIdC(IdentifierC) 290 () 291 (:index t) 292 (:documentation "A subject-identifier that contains an uri-value and an 293 association to PersistentIdAssociationC's which are in 294 turn associated with TopicC's.")) 295 296 297 (defpclass ItemIdentifierC(IdentifierC) 298 () 299 (:index t) 300 (:documentation "An item-identifier that contains an uri-value and an 301 association to ItemIdAssociationC's which are in turn 302 associated with RiefiableConstructC's.")) 303 304 305 ;;; reifiables ... 306 (defpclass ReifiableConstructC(TopicMapConstructC) 307 ((item-identifiers :associate (ItemIdAssociationC parent-construct) 308 :inherit t 309 :documentation "A relation to all item-identifiers of 310 this construct.") 311 (reifier :associate (ReifierAssociationC reifiable-construct) 312 :inherit t 313 :documentation "A relation to a reifier-topic.")) 314 (:documentation "Reifiable constructs as per TMDM.")) 315 316 317 (defpclass AssociationC(ReifiableConstructC ScopableC TypableC 318 VersionedConstructC) 319 ((roles :associate (RoleAssociationC parent-construct) 320 :documentation "Contains all association-objects of all roles this 321 association contains.") 322 (in-topicmaps :associate (TopicMapC associations) 323 :many-to-many t 324 :documentation "List of all topic maps this association is 325 part of")) 326 (:index t) 327 (:documentation "Association in a Topic Map")) 328 329 330 (defpclass RoleC(ReifiableConstructC TypableC) 331 ((parent :associate (RoleAssociationC role) 332 :documentation "Associates this object with a role-association.") 333 (player :associate (PlayerAssociationC parent-construct) 334 :documentation "Associates this object with a player-association."))) 335 336 337 (elephant:defpclass TopicMapC (ReifiableConstructC VersionedConstructC) 338 ((topics :associate (TopicC in-topicmaps) 339 :many-to-many t 340 :accessor topics 341 :documentation "List of topics that explicitly belong to this TM.") 342 (associations :associate (AssociationC in-topicmaps) 343 :many-to-many t 344 :accessor associations 345 :documentation "List of associations that belong to this TM.")) 346 (:documentation "Represnets a topic map.")) 347 348 349 (defpclass TopicC (ReifiableConstructC VersionedConstructC) 350 ((topic-identifiers :associate (TopicIdAssociationC parent-construct) 351 :documentation "Contains all association objects that 352 relate a topic with its actual 353 topic-identifiers.") 354 (psis :associate (PersistentIdAssociationC parent-construct) 355 :documentation "Contains all association objects that relate a topic 356 with its actual psis.") 357 (locators :associate (SubjectLocatorAssociationC parent-construct) 358 :documentation "Contains all association objects that relate a 359 topic with its actual subject-lcoators.") 360 (names :associate (NameAssociationC parent-construct) 361 :documentation "Contains all association objects that relate a topic 362 with its actual names.") 363 (occurrences :associate (OccurrenceAssociationC parent-construct) 364 :documentation "Contains all association objects that relate a 365 topic with its actual occurrences.") 366 (player-in-roles :associate (PlayerAssociationC player-topic) 367 :documentation "Contains all association objects that relate 368 a topic that is a player with its role.") 369 (used-as-type :associate (TypeAssociationC type-topic) 370 :documentation "Contains all association objects that relate a 371 topic that is a type with its typable obejct.") 372 (used-as-theme :associate (ScopeAssociationC theme-topic) 373 :documentation "Contains all association objects that relate a 374 topic that is a theme with its scoppable 375 object.") 376 (reified-construct :associate (ReifierAssociationC reifier-topic) 377 :documentation "Contains all association objects that 378 relate a topic that is a reifier with 379 its reified object.") 380 (in-topicmaps :associate (TopicMapC topics) 381 :many-to-many t 382 :documentation "List of all topic maps this topic is part of.")) 383 (:index t) 384 (:documentation "Represents a TM topic.")) 385 386 387 388 ;;; characteristics ... 389 (defpclass CharacteristicC(ReifiableConstructC ScopableC TypableC) 390 ((parent :associate (CharacteristicAssociationC characteristic) 391 :inherit t 392 :documentation "Assocates the characterist obejct with the 393 parent-association.") 394 (charvalue :initarg :charvalue 395 :accessor charvalue 396 :type string 397 :inherit t 398 :initform "" 399 :index t 400 :documentation "Contains the actual data of this object.")) 401 (:documentation "Scoped characteristic of a topic (meant to be used 402 as an abstract class).")) 403 404 405 (defpclass OccurrenceC(CharacteristicC DatatypableC) 406 () 407 (:documentation "Represents a TM occurrence.")) 408 409 410 (defpclass NameC(CharacteristicC) 411 ((variants :associate (VariantAssociationC parent-construct) 412 :documentation "Associates this obejct with varian-associations.")) 413 (:documentation "Scoped name of a topic.")) 414 415 416 (defpclass VariantC(CharacteristicC DatatypableC) 417 () 418 (:documentation "Represents a TM variant.")) 419 420 421 ;;; versioned associations ... 422 (defpclass VersionedAssociationC(VersionedConstructC) 423 () 424 (:documentation "An abstract base class for all versioned associations.")) 425 426 427 (defpclass TypeAssociationC(VersionedAssociationC) 428 ((type-topic :initarg :type-topic 429 :accessor type-topic 430 :initform (error (make-missing-argument-condition "From TypeAssociationC(): type-topic must be set" 'type-topic ':type-topic)) 431 :associate TopicC 432 :documentation "Associates this object with a topic that is used 433 as type.") 434 (typable-construct :initarg :typable-construct 435 :accessor typable-construct 436 :initform (error (make-missing-argument-condition "From TypeAssociationC(): typable-construct must be set" 'typable-construct ':typable-construct)) 437 :associate TypableC 438 :documentation "Associates this object with the typable 439 construct that is typed by the 440 type-topic.")) 441 (:documentation "This class associates topics that are used as type for 442 typable constructcs. Additionally there are stored some 443 version-infos.")) 444 445 446 (defpclass ScopeAssociationC(VersionedAssociationC) 447 ((theme-topic :initarg :theme-topic 448 :accessor theme-topic 449 :initform (error (make-missing-argument-condition "From ScopeAssociationC(): theme-topic must be set" 'theme-topic ':theme-topic)) 450 :associate TopicC 451 :documentation "Associates this opbject with a topic that is a 452 scopable construct.") 453 (scopable-construct :initarg :scopable-construct 454 :accessor scopable-construct 455 :initform (error (make-missing-argument-condition "From ScopeAssociationC(): scopable-construct must be set" 'scopable-construct ':scopable-construct)) 456 :associate ScopableC 457 :documentation "Associates this object with the socpable 458 construct that is scoped by the 459 scope-topic.")) 460 (:documentation "This class associates topics that are used as scope with 461 scopable construtcs. Additionally there are stored some 462 version-infos")) 463 464 465 (defpclass ReifierAssociationC(VersionedAssociationC) 466 ((reifiable-construct :initarg :reifiable-construct 467 :accessor reifiable-construct 468 :initform (error (make-missing-argument-condition "From ReifierAssociation(): reifiable-construct must be set" 'reifiable-construct ':reifiable-construct)) 469 :associate ReifiableConstructC 470 :documentation "The actual construct which is reified 471 by a topic.") 472 (reifier-topic :initarg :reifier-topic 473 :accessor reifier-topic 474 :initform (error (make-missing-argument-condition "From ReifierAssociationC(): reifier-topic must be set" 'reifier-topic ':reifier-topic)) 475 :associate TopicC 476 :documentation "The reifier-topic that reifies the 477 reifiable-construct.")) 478 (:documentation "A versioned-association that relates a reifiable-construct 479 with a topic.")) 480 481 482 ;;; pointer associations ... 483 (defpclass PointerAssociationC (VersionedAssociationC) 484 ((identifier :initarg :identifier 485 :accessor identifier 486 :inherit t 487 :initform (error (make-missing-argument-condition "From PointerAssociationC(): identifier must be set" 'identifier ':identifier)) 488 :associate PointerC 489 :documentation "The actual data that is associated with 490 the pointer-association's parent.")) 491 (:documentation "An abstract base class for all versioned 492 pointer-associations.")) 493 494 495 (defpclass SubjectLocatorAssociationC(PointerAssociationC) 496 ((parent-construct :initarg :parent-construct 497 :accessor parent-construct 498 :initform (error (make-missing-argument-condition "From SubjectLocatorAssociationC(): parent-construct must be set" 'parent-construct ':parent-symbol)) 499 :associate TopicC 500 :documentation "The actual topic which is associated 501 with the subject-locator.")) 502 (:documentation "A pointer that associates subject-locators, versions 503 and topics.")) 504 505 506 (defpclass PersistentIdAssociationC(PointerAssociationC) 507 ((parent-construct :initarg :parent-construct 508 :accessor parent-construct 509 :initform (error (make-missing-argument-condition "From PersistentIdAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 510 :associate TopicC 511 :documentation "The actual topic which is associated 512 with the subject-identifier/psi.")) 513 (:documentation "A pointer that associates subject-identifiers, versions 514 and topics.")) 515 516 517 (defpclass TopicIdAssociationC(PointerAssociationC) 518 ((parent-construct :initarg :parent-construct 519 :accessor parent-construct 520 :initform (error (make-missing-argument-condition "From TopicIdAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 521 :associate TopicC 522 :documentation "The actual topic which is associated 523 with the topic-identifier.")) 524 (:documentation "A pointer that associates topic-identifiers, versions 525 and topics.")) 526 527 528 (defpclass ItemIdAssociationC(PointerAssociationC) 529 ((parent-construct :initarg :parent-construct 530 :accessor parent-construct 531 :initform (error (make-missing-argument-condition "From ItemIdAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 532 :associate ReifiableConstructC 533 :documentation "The actual parent which is associated 534 with the item-identifier.")) 535 (:documentation "A pointer that associates item-identifiers, versions 536 and reifiable-constructs.")) 537 538 539 ;;; characteristic associations ... 540 (defpclass CharacteristicAssociationC(VersionedAssociationC) 541 ((characteristic :initarg :characteristic 542 :accessor characteristic 543 :inherit t 544 :initform (error (make-missing-argument-condition "From CharacteristicCAssociation(): characteristic must be set" 'characteristic ':characteristic)) 545 :associate CharacteristicC 546 :documentation "Associates this object with the actual 547 characteristic object.")) 548 (:documentation "An abstract base class for all association-objects that 549 associates characteristics with topics.")) 550 551 552 (defpclass VariantAssociationC(CharacteristicAssociationC) 553 ((parent-construct :initarg :parent-construct 554 :accessor parent-construct 555 :initform (error (make-missing-argument-condition "From VariantAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 556 :associate NameC 557 :documentation "Associates this object with a name.")) 558 (:documentation "Associates variant objects with name obejcts. 559 Additionally version-infos are stored.")) 560 561 562 (defpclass NameAssociationC(CharacteristicAssociationC) 563 ((parent-construct :initarg :parent-construct 564 :accessor parent-construct 565 :initform (error (make-missing-argument-condition "From NameAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 566 :associate TopicC 567 :documentation "Associates this object with a topic.")) 568 (:documentation "Associates name objects with their parent topics. 569 Additionally version-infos are stored.")) 570 571 572 (defpclass OccurrenceAssociationC(CharacteristicAssociationC) 573 ((parent-construct :initarg :parent-construct 574 :accessor parent-construct 575 :initform (error (make-missing-argument-condition "From OccurrenceAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 576 :associate TopicC 577 :documentation "Associates this object with a topic.")) 578 (:documentation "Associates occurrence objects with their parent topics. 579 Additionally version-infos are stored.")) 580 581 582 ;;; roles/association associations ... 583 (defpclass PlayerAssociationC(VersionedAssociationC) 584 ((player-topic :initarg :player-topic 585 :accessor player-topic 586 :associate TopicC 587 :initform (error (make-missing-argument-condition "From PlayerAssociationC(): player-topic must be set" 'player-topic ':player-topic)) 588 :documentation "Associates this object with a topic that is 589 a player.") 590 (parent-construct :initarg :parent-construct 591 :accessor parent-construct 592 :associate RoleC 593 :initform (error (make-missing-argument-condition "From PlayerAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 594 :documentation "Associates this object with the parent-association.")) 595 (:documentation "This class associates roles and their player in given 596 revisions.")) 597 598 599 (defpclass RoleAssociationC(VersionedAssociationC) 600 ((role :initarg :role 601 :accessor role 602 :associate RoleC 603 :initform (error (make-missing-argument-condition "From RoleAssociationC(): role must be set" 'role ':role)) 604 :documentation "Associates this objetc with a role-object.") 605 (parent-construct :initarg :parent-construct 606 :accessor parent-construct 607 :associate AssociationC 608 :initform (error (make-missing-argument-condition "From RoleAssociationC(): parent-construct must be set" 'parent-construct ':parent-construct)) 609 :documentation "Assocates thius object with an 610 association-object.")) 611 (:documentation "Associates roles with assoications and adds some 612 version-infos between these realtions.")) 613 614 615 ;;; some helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 616 (defun make-duplicate-identifier-condition (message uri) 617 "Returns an duplicate-identifier-condition with the passed arguments." 618 (make-condition 'duplicate-identifier-error 619 :message message 620 :uri uri)) 621 622 623 (defun make-object-not-found-condition (message) 624 "Returns an object-not-found-condition with the passed arguments." 625 (make-condition 'object-not-found-error 626 :message message)) 627 628 629 (defun make-tm-reference-condition (message referenced-construct 630 existing-reference new-reference) 631 "Returns a tm-reference-condition with the passed arguments." 632 (make-condition 'tm-reference-error 633 :message message 634 :referenced-construct referenced-construct 635 :existing-reference existing-reference 636 :new-reference new-reference)) 637 638 639 (defun make-not-mergable-condition (message construct-1 construct-2) 640 "Returns a not-mergable-condition with the passed arguments." 641 (make-condition 'not-mergable-error 642 :message message 643 :construct-1 construct-1 644 :construct-2 construct-2)) 645 646 647 (defun make-missing-argument-condition (message argument-symbol function-symbol) 648 "Returns a missing-argument-condition with the passed arguments." 649 (make-condition 'missing-argument-error 650 :message message 651 :argument-symbol argument-symbol 652 :function-symbol function-symbol)) 653 654 655 (defgeneric get-most-recent-versioned-assoc (construct slot-symbol) 656 (:documentation "Returns the most recent VersionedAssociationC 657 object.") 658 (:method ((construct TopicMapConstructC) (slot-symbol Symbol)) 659 (let ((all-assocs (slot-p construct slot-symbol))) 660 (let ((zero-assoc 661 (find-if #'(lambda(assoc) 662 (= (end-revision 663 (get-most-recent-version-info assoc)) 0)) 664 all-assocs))) 665 (if zero-assoc 666 zero-assoc 667 (let ((ordered-assocs 668 (sort all-assocs 669 #'(lambda(x y) 670 (> (end-revision 671 (get-most-recent-version-info x)) 672 (end-revision 673 (get-most-recent-version-info y))))))) 674 (when ordered-assocs 675 (first ordered-assocs)))))))) 676 677 678 (defun get-latest-topic-by-psi (topic-psi) 679 "Returns the latest topic bound to the PersistentIdC 680 object corresponding to the given uri." 681 (declare (String topic-psi)) 682 (let ((psi-inst 683 (elephant:get-instance-by-value 684 'PersistentIdC 'uri topic-psi))) 685 (let ((latest-va 686 (get-most-recent-versioned-assoc 687 psi-inst 'identified-construct))) 688 (when (and latest-va (versions latest-va)) 689 (identified-construct 690 psi-inst :revision (start-revision (first (versions latest-va)))))))) 691 692 693 (defun get-db-instances-by-class (class-symbol &key (revision *TM-REVISION*)) 694 "Returns all instances of the given type and the given revision that are 695 stored in the db." 696 (declare (symbol class-symbol) (type (or null integer) revision)) 697 (let ((db-instances (elephant:get-instances-by-class class-symbol))) 698 (let ((filtered-instances (remove-if-not #'(lambda(inst) 699 (typep inst class-symbol)) 700 db-instances))) 701 (if revision 702 (remove-if #'null 703 (map 'list #'(lambda(inst) 704 (find-item-by-revision inst revision)) 705 filtered-instances)) 706 filtered-instances)))) 707 708 709 (defun get-all-topics (&optional (revision *TM-REVISION*)) 710 (get-db-instances-by-class 'TopicC :revision revision)) 711 712 713 (defun get-all-associations (&optional (revision *TM-REVISION*)) 714 (get-db-instances-by-class 'AssociationC :revision revision)) 715 716 717 (defun get-all-tms (&optional (revision *TM-REVISION*)) 718 (get-db-instances-by-class 'TopicMapC :revision revision)) 719 720 721 (defun find-version-info (versioned-constructs 722 &key (sort-function #'<) (sort-key 'start-revision)) 723 "Returns all version-infos sorted by the function sort-function which is 724 applied on the slot sort-key." 725 (declare (list versioned-constructs)) 726 (let ((vis 727 (sort 728 (loop for vc in versioned-constructs 729 append (versions vc)) 730 sort-function :key sort-key))) 731 (when vis 732 (first vis)))) 733 734 735 (defun rec-remf (plist keyword) 736 "Calls remf for the past plist with the given keyword until 737 all key-value-pairs corresponding to the passed keyword were removed." 738 (declare (list plist) (keyword keyword)) 739 (loop while (getf plist keyword) 740 do (remf plist keyword)) 741 plist) 742 743 744 (defun get-item-by-content (content &key (revision *TM-REVISION*)) 745 "Finds characteristics by their (atomic) content." 746 (flet 747 ((get-existing-instances (class-symbol) 748 (delete-if-not 749 #'(lambda (constr) 750 (find-item-by-revision constr revision)) 751 (elephant:get-instances-by-value class-symbol 'charvalue content)))) 752 (nconc (get-existing-instances 'OccurenceC) 753 (get-existing-instances 'NameC) 754 (get-existing-instances 'VariantC)))) 755 165 756 166 757 (defmacro with-revision (revision &rest body) 167 758 `(let 168 ((*TM-REVISION* ,revision)) 169 ;(format t "*TM-REVISION* is ~a~&" *TM-REVISION*) 170 ,@body)) 171 172 173 (defmacro slot-predicate (instance slot) 174 (let 175 ((inst-name (gensym)) 176 (slot-name (gensym))) 177 `(let 178 ((,inst-name ,instance) 179 (,slot-name ,slot)) 180 (and (slot-boundp ,inst-name ,slot-name) 181 (slot-value ,inst-name ,slot-name))))) 182 183 (defmacro delete-1-n-association (instance slot) 184 (let 185 ((inst-name (gensym)) 186 (slot-name (gensym))) 187 `(let 188 ((,inst-name ,instance) 189 (,slot-name ,slot)) 190 (when (slot-predicate ,inst-name ,slot-name) 191 (elephant:remove-association ,inst-name ,slot-name (slot-value ,inst-name ,slot-name)))))) 192 193 (defun xor (a1 a2) 194 (and (or a1 a2) (not (and a1 a2))) 195 ) 196 197 (defun remove-nil-values (plist) 198 (let 199 ((result nil)) 200 (do* ((rest plist (cddr rest)) 201 (key (first rest) (first rest)) 202 (val (second rest) (second rest))) 203 ((null rest)) 204 (when val 205 (pushnew val result) 206 (pushnew key result))) 207 result)) 759 ((*TM-REVISION* ,revision)) 760 ,@body)) 761 762 763 (defun slot-p (instance slot-symbol) 764 "Returns t if the slot depending on slot-symbol is bound and not nil." 765 (if (slot-boundp instance slot-symbol) 766 (let ((value (slot-value instance slot-symbol))) 767 (when value 768 value)) 769 ;elephant-relations are handled separately, since slot-boundp does not 770 ;work here 771 (handler-case (let ((value (slot-value instance slot-symbol))) 772 (when value 773 value)) 774 (error () nil)))) 775 776 777 (defun delete-1-n-association(instance slot-symbol) 778 (when (slot-p instance slot-symbol) 779 (remove-association 780 instance slot-symbol (slot-value instance slot-symbol)))) 781 782 783 (defgeneric delete-construct (construct) 784 (:documentation "Drops recursively construct and all its dependent objects 785 from the elephant store.")) 786 787 788 (defmethod delete-construct ((construct elephant:persistent)) 789 nil) 790 791 792 (defmethod delete-construct :after ((construct elephant:persistent)) 793 (drop-instance construct)) 794 795 796 (defun filter-slot-value-by-revision (construct slot-symbol 797 &key (start-revision 798 0 start-revision-provided-p)) 799 (declare (symbol slot-symbol) (integer start-revision)) 800 (let ((revision 801 (cond (start-revision-provided-p 802 start-revision) 803 ((boundp '*TM-REVISION*) 804 *TM-REVISION*) 805 (t 0))) 806 (properties (slot-p construct slot-symbol))) 807 (cond ((not properties) 808 nil) ;no properties were found -> nil 809 ((= 0 revision) 810 (remove-if #'null 811 (map 'list #'find-most-recent-revision properties))) 812 (t 813 (remove-if #'null 814 (map 'list #'(lambda(prop) 815 (find-item-by-revision prop revision)) 816 properties)))))) 817 208 818 209 819 (defun get-revision () … … 211 821 (get-universal-time)) 212 822 213 (defgeneric delete-construct (construct) 214 (:documentation "drops recursively construct and all its dependent objects from the elephant store")) 215 216 (defmethod delete-construct ((construct elephant:persistent)) 217 nil) 218 219 (defmethod delete-construct :after ((construct elephant:persistent)) 220 (elephant:drop-instance construct)) 221 222 (defgeneric find-all-equivalent (construct) 223 (:method ((construct t)) nil) 224 (:documentation "searches an existing object that is equivalent (but not identical) to construct")) 225 226 227 ;;;;;;;;;;;;;; 228 ;; 229 ;; VersionInfoC 230 231 232 (elephant:defpclass VersionInfoC () 233 ((start-revision :accessor start-revision 234 :initarg :start-revision 235 :type integer 236 :initform 0 ;TODO: for now 237 :documentation "The first revison this AssociationC instance is associated with.") 238 (end-revision :accessor end-revision 239 :initarg :end-revision 240 :type integer 241 :initform 0 ;TODO: for now 242 :documentation "The first revison this AssociationC instance is no longer associated with.") 243 (versioned-construct :associate TopicMapConstructC 244 :accessor versioned-construct 245 :initarg :versioned-construct 246 :documentation "reifiable construct that is described by this info")) 247 (:documentation "Version Info for individual revisions")) 248 249 (defgeneric versioned-construct-p (vi) 250 (:documentation "t if this version info is already bound to a TM construct") 251 (:method ((vi VersionInfoC)) (slot-predicate vi 'versioned-construct))) 252 253 (defmethod delete-construct :before ((vi VersionInfoC)) 254 (delete-1-n-association vi 'versioned-construct)) 255 256 (defgeneric get-most-recent-version-info (construct)) 257 258 259 ;;;;;;;;;;;;;; 260 ;; 261 ;; ItemIdentifierC 262 263 (elephant:defpclass ItemIdentifierC (IdentifierC) 264 () 265 (:index t) 266 (:documentation "Represents an item identifier")) 267 268 269 ;;;;;;;;;;;;;; 270 ;; 271 ;; SubjectLocator 272 273 (elephant:defpclass SubjectLocatorC (IdentifierC) 274 ((identified-construct :accessor identified-construct 275 :initarg :identified-construct 276 :associate TopicC)) 277 (:index t) 278 (:documentation "Represents a subject locator")) 279 280 281 ;;;;;;;;;;;;;; 282 ;; 283 ;; IdentifierC 284 285 (elephant:defpclass IdentifierC (PointerC) 286 () 287 (:documentation "Abstract base class for ItemIdentifierC and 288 PersistentIdC, primarily in view of the equality rules")) 289 290 291 ;;;;;;;;;;;;;; 292 ;; 293 ;; PointerC 294 295 (elephant:defpclass PointerC (TopicMapConstructC) 296 ((uri :accessor uri 297 :initarg :uri 298 :type string 299 :initform (error "The uri must be set for a pointer") 300 :index t) 301 (identified-construct :accessor identified-construct 302 :initarg :identified-construct 303 :associate ReifiableConstructC)) 304 (:documentation "Abstract base class for all types of pointers and identifiers")) 305 306 (defmethod delete-construct :before ((construct PointerC)) 307 (delete-1-n-association construct 'identified-construct)) 308 309 (defmethod find-all-equivalent ((construct PointerC)) 310 (delete construct 311 (elephant:get-instances-by-value (class-of construct) 312 'uri 313 (uri construct)) 314 :key #'internal-id)) 315 (defgeneric uri-p (construct) 316 (:documentation "Check if the slot uri is bound in an identifier and not nil") 317 (:method ((identifier PointerC)) (slot-predicate identifier 'uri))) 318 319 (defgeneric identified-construct-p (construct) 320 (:documentation "Check if the slot identified-construct is bound in an identifier and not nil") 321 (:method ((identifier PointerC)) (slot-predicate identifier 'identified-construct))) 322 323 (defmethod print-object ((identifier PointerC) stream) 324 (format stream 325 "~a(href: ~a; Construct: ~a)" 326 (class-name (class-of identifier)) 327 (if (uri-p identifier) 328 (uri identifier) 329 "URI UNDEFINED") 330 (if (identified-construct-p identifier) 331 (identified-construct identifier) 332 "SLOT UNBOUND"))) 333 334 (defmethod equivalent-constructs ((identifier1 PointerC) (identifier2 PointerC)) 335 (string= (uri identifier1) (uri identifier2))) 336 337 (defmethod initialize-instance :around ((identifier PointerC) &key 338 (start-revision (error "Start revision must be present") ) 339 (end-revision 0)) 340 (call-next-method) 341 (add-to-version-history identifier 342 :start-revision start-revision 343 :end-revision end-revision) 344 identifier) 345 346 347 ;;;;;;;;;;;;;; 348 ;; 349 ;; TopicMapConstrucC 350 351 352 (elephant:defpclass TopicMapConstructC () 353 ((versions :associate (VersionInfoC versioned-construct) 354 :accessor versions 355 :initarg :versions 356 :documentation "version infos for former versions of this reifiable construct"))) 357 358 ;TODO: if, one day, we allow merges of already existing constructs, we'll need 359 ;a tree of predecessors rather then just a list of versions. A case in point 360 ;may be if a newly imported topic carries the PSIs of two existing topics, 361 ;thereby forcing a merge post factum" 362 363 (defmethod delete-construct :before ((construct TopicMapConstructC)) 364 (dolist (versioninfo (versions construct)) 365 (delete-construct versioninfo))) 366 367 368 (defgeneric add-to-version-history (construct &key start-revision end-revision) 369 (:documentation "Add version history to a topic map construct")) 370 371 (defmethod add-to-version-history ((construct TopicMapConstructC) 372 &key 373 (start-revision (error "Start revision must be present") ) 374 (end-revision 0)) 375 "Adds relevant information to a construct's version info" 376 (let 377 ((current-version-info 378 (get-most-recent-version-info construct))) 379 (cond 380 ((and current-version-info 381 (= (end-revision current-version-info) start-revision)) ;the item was just marked as deleted 382 (setf (end-revision current-version-info) 0) ;just revitalize it, do not create a new version 383 current-version-info) ;TODO: this is not quite correct, the topic 384 ;might be recreated with new item 385 ;identifiers. Consider adding a new parameter 386 ;"revitalize" 387 ((and 388 current-version-info 389 (= (end-revision current-version-info) 0)) 390 (setf (end-revision current-version-info) start-revision) 391 (make-instance 392 'VersionInfoC 393 :start-revision start-revision 394 :end-revision end-revision 395 :versioned-construct construct)) 396 (t 397 (make-instance 398 'VersionInfoC 399 :start-revision start-revision 400 :end-revision end-revision 401 :versioned-construct construct))))) 402 403 (defgeneric revision (constr) 404 (:documentation "Essentially a convenience method for start-revision")) 405 406 (defmethod revision ((constr TopicMapConstructC)) 407 (start-revision constr)) 408 409 (defmethod (setf revision) ((constr TopicMapConstructC) (revision integer)) 410 (setf (start-revision constr) revision)) 411 412 413 (defgeneric find-item-by-revision (constr revision) 414 (:documentation "Get a given version of a construct (if any, nil if none can be found)")) 415 416 (defmethod find-item-by-revision ((constr TopicMapConstructC) (revision integer)) 417 (cond 418 ((= revision 0) 419 (find-most-recent-revision constr)) 420 (t 421 (when (find-if 422 (lambda(version) 423 (and (>= revision (start-revision version)) 424 (or 425 (< revision (end-revision version)) 426 (= 0 (end-revision version))))) 427 (versions constr)) 428 constr)))) 429 430 (defgeneric find-most-recent-revision (construct) 431 (:documentation "Get the most recent version of a construct (nil if 432 the construct doesn't have versions yet or not anymore)")) 433 434 (defmethod find-most-recent-revision ((construct TopicMapConstructC)) 435 (when (find 0 (versions construct) :key #'end-revision) 436 construct)) 437 438 (defmethod delete-construct :before ((construct TopicMapConstructC)) 439 (dolist (versionInfo (versions construct)) 440 (delete-construct versionInfo))) 441 442 443 (defgeneric check-for-duplicate-identifiers (top) 823 824 (defun string-integer-p (integer-as-string) 825 "Returns t if the passed string can be parsed to an integer." 826 (handler-case (when (parse-integer integer-as-string) 827 t) 828 (condition () nil))) 829 830 831 (defun merge-all-constructs(constructs-to-be-merged &key (revision *TM-REVISION*)) 832 "Merges all constructs contained in the given list." 833 (declare (list constructs-to-be-merged)) 834 (cond ((null constructs-to-be-merged) 835 nil) 836 ((= (length constructs-to-be-merged) 1) 837 (first constructs-to-be-merged)) 838 (t 839 (let ((constr-1 (first constructs-to-be-merged)) 840 (constr-2 (second constructs-to-be-merged)) 841 (tail (subseq constructs-to-be-merged 2))) 842 (let ((merged-constr 843 (merge-constructs constr-1 constr-2 :revision revision))) 844 (merge-all-constructs (append (list merged-constr) 845 tail))))))) 846 847 848 (defgeneric internal-id (construct) 849 (:documentation "Returns the internal id that uniquely identifies a 850 construct (currently simply its OID).")) 851 852 853 (defmethod internal-id ((construct TopicMapConstructC)) 854 (slot-value construct (find-symbol "OID" 'elephant))) 855 856 857 (defun string-starts-with (str prefix) 858 "Checks if string str starts with a given prefix." 859 (declare (string str prefix)) 860 (string= str prefix :start1 0 :end1 861 (min (length prefix) 862 (length str)))) 863 864 865 ;;; generic definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 866 (defgeneric mark-as-deleted (construct &key source-locator revision) 867 (:documentation "Mark a construct as deleted if it comes from the source 868 indicated by source-locator")) 869 870 871 (defgeneric marked-as-deleted-p (construct) 872 (:documentation "Returns t if the construct was marked-as-deleted.")) 873 874 875 (defgeneric find-self-or-equal (construct parent-construct &key revision) 876 (:documentation "Returns the construct 'construct' if is owned by the 877 parent-construct or an equal construct or nil if there 878 is no equal one.")) 879 880 881 (defgeneric merge-if-equivalent (new-characteristic parent-construct 882 &key revision) 883 (:documentation "Merges the new characteristic/role with one equivalent of the 884 parent's charateristics/roles instead of adding the entire new 885 characteristic/role to the parent.")) 886 887 888 (defgeneric parent (construct &key revision) 889 (:documentation "Returns the parent construct of the passed object that 890 corresponds with the given revision. The returned construct 891 can be a TopicC or a NameC.")) 892 893 894 (defgeneric delete-if-not-referenced (construct) 895 (:documentation "Calls delete-construct for the given object if it is 896 not referenced by any other construct.")) 897 898 899 (defgeneric add-characteristic (construct characteristic &key revision) 900 (:documentation "Adds the passed characterisitc to the given topic by calling 901 add-name or add-occurrences. 902 Variants are added to names by calling add-name.")) 903 904 905 (defgeneric private-delete-characteristic (construct characteristic &key revision) 906 (:documentation "Deletes the passed characteristic of the given topic by 907 calling delete-name or delete-occurrence. 908 Variants are deleted from names by calling delete-variant.")) 909 910 911 (defgeneric delete-characteristic (construct characteristic &key revision) 912 (:documentation "See private-delete-characteristic but adds the parent 913 (if it is a variant also the parent's parent) to the 914 version history of this call's revision")) 915 916 917 (defgeneric find-oldest-construct (construct-1 construct-2) 918 (:documentation "Returns the construct which owns the oldes version info. 919 If a construct is not a versioned construct the oldest 920 association determines the construct's version info.")) 921 922 923 (defgeneric merge-constructs (construct-1 construct-2 &key revision) 924 (:documentation "Merges two constructs of the same type if they are 925 mergable. The latest construct will be marked as deleted 926 The older one gets all characteristics of the marked as 927 deleted one. All referenced constructs are also updated 928 with the changeds that are caused by this operation.")) 929 930 931 (defgeneric parent-delete-parent (construct parent-construct &key revision) 932 (:documentation "Sets the assoication-object between the passed 933 constructs as marded-as-deleted.")) 934 935 936 (defgeneric delete-parent (construct parent-construct &key revision) 937 (:documentation "See private-delete-parent but adds the parent to 938 the given version.")) 939 940 941 (defgeneric add-parent (construct parent-construct &key revision) 942 (:documentation "Adds the parent-construct (TopicC or NameC) in form of 943 a corresponding association to the given object.")) 944 945 946 (defgeneric find-item-by-revision (construct revision 947 &optional parent-construct) 948 (:documentation "Returns the given object if it exists in the passed 949 version otherwise nil. 950 Constructs that exist to be owned by parent-constructs 951 must provide their parent-construct to get the corresponding 952 revision of the relationship between the construct itself and 953 its parent-construct.")) 954 955 956 (defgeneric check-for-duplicate-identifiers (construct &key revision) 444 957 (:documentation "Check for possibly duplicate identifiers and signal an 445 958 duplicate-identifier-error is such duplicates are found")) 446 959 447 (defmethod check-for-duplicate-identifiers ((construct TopicMapConstructC)) 448 (declare (ignore construct)) 449 ;do nothing 450 ) 451 452 (defgeneric filter-slot-value-by-revision (construct slot-name &key start-revision) 453 (:documentation "filter slot values by a given revision that is 454 either provided directly through the keyword argument start-revision 455 or through a bound variable named '*TM-REVISION*'")) 456 457 (defmethod filter-slot-value-by-revision ((construct TopicMapConstructC) (slot-name symbol) &key (start-revision 0 start-revision-provided-p)) 458 (let 459 ((revision ;avoids warnings about undefined variables 460 (cond 461 (start-revision-provided-p 462 start-revision) 463 ((boundp '*TM-REVISION*) 464 (symbol-value '*TM-REVISION*)) 465 (t 0))) 466 (properties (slot-value construct slot-name))) 467 ;(format t "revision in filter-slot-value-by-revision is ~a~&" revision) 468 (cond 469 ((not properties) 470 nil) ;if we don't have any properties, we don't have to worry 471 ;about revisions 472 ((= 0 revision) 473 (remove 474 nil 475 (map 'list #'find-most-recent-revision 476 properties))) 477 (t 478 (remove nil 479 (map 'list 480 (lambda (constr) 481 (find-item-by-revision constr revision)) 482 properties)))))) 483 484 (defgeneric make-construct (classsymbol &key start-revision &allow-other-keys) 485 (:documentation "create a new topic map construct if necessary or 486 retrieve an equivalent one if available and update the revision 487 history accordingly. Return the object in question. Methods use 488 specific keyword arguments for their purpose")) 489 490 (defmethod make-construct ((classsymbol symbol) &rest args 491 &key start-revision) 492 (let* 493 ((cleaned-args (remove-nil-values args)) 494 (new-construct (apply #'make-instance classsymbol cleaned-args)) 495 (existing-construct (first (find-all-equivalent new-construct)))) 496 (if existing-construct 497 (progn 498 ;change over new item identifiers to the old construct 499 ;the version-history is also changed if the construct was 500 ;marked-as-deleted before 501 (when (or (copy-item-identifiers new-construct existing-construct) 502 (not (find-most-recent-revision existing-construct))) 503 (add-to-version-history existing-construct 504 :start-revision start-revision)) 505 506 (delete-construct new-construct) 507 existing-construct) 508 (progn 509 (add-to-version-history new-construct :start-revision start-revision) 510 (check-for-duplicate-identifiers new-construct) 511 new-construct)))) 512 513 (defmethod get-most-recent-version-info ((construct TopicMapConstructC)) 960 961 (defgeneric get-all-identifiers-of-construct (construct &key revision) 962 (:documentation "Get all identifiers that a given construct has")) 963 964 965 (defgeneric get-all-characteristics (parent-construct characteristic-symbol) 966 (:documentation "Returns all characterisitcs of the passed type the parent 967 construct was ever associated with.")) 968 969 970 (defgeneric equivalent-construct (construct &key start-revision 971 &allow-other-keys) 972 (:documentation "Returns t if the passed construct is equivalent to the passed 973 key arguments (TMDM equality rules). Parent-equality is not 974 checked in this methods, so the user has to pass children of 975 the same parent.")) 976 977 978 (defgeneric equivalent-constructs (construct-1 construct-2 &key revision) 979 (:documentation "Returns t if the passed constructs are equivalent to each 980 other (TMDM equality rules). Parent-equality is not 981 checked in this methods, so the user has to pass children of 982 the same parent.")) 983 984 985 (defgeneric get-most-recent-version-info (construct) 986 (:documentation "Returns the latest VersionInfoC object of the passed 987 versioned construct. 988 The latest construct is either the one with 989 end-revision=0 or with the highest end-revision value.")) 990 991 (defgeneric owned-p (construct) 992 (:documentation "Returns t if the passed construct is referenced by a parent 993 TM construct.")) 994 995 996 (defgeneric in-topicmaps (construct &key revision) 997 (:documentation "Returns all TopicMaps-obejcts where the construct is 998 contained in.")) 999 1000 1001 (defgeneric add-to-tm (construct construct-to-add) 1002 (:documentation "Adds a TM construct (TopicC or AssociationC) to the TM.")) 1003 1004 1005 (defgeneric delete-from-tm (construct construct-to-delete) 1006 (:documentation "Deletes a TM construct (TopicC or AssociationC) from 1007 the TM.")) 1008 1009 1010 1011 ;;; generic functions/accessors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1012 ;;; VersionInfocC 1013 (defmethod delete-construct :before ((version-info VersionInfoC)) 1014 (delete-1-n-association version-info 'versioned-construct)) 1015 1016 1017 ;;; VersionedConstructC 1018 (defgeneric exist-in-version-history-p (versioned-construct) 1019 (:documentation "Returns t if the passed construct does not exist in any 1020 revision, i.e. the construct has no version-infos or exactly 1021 one whose start-revision is equal to its end-revision.") 1022 (:method ((versioned-construct VersionedConstructC)) 1023 (or (not (versions versioned-construct)) 1024 (and (= (length (versions versioned-construct)) 1) 1025 (= (start-revision (first (versions versioned-construct))) 1026 (end-revision (first (versions versioned-construct)))))))) 1027 1028 1029 (defmethod find-oldest-construct ((construct-1 VersionedConstructC) 1030 (construct-2 VersionedConstructC)) 1031 (let ((vi-1 (find-version-info (list construct-1))) 1032 (vi-2 (find-version-info (list construct-2)))) 1033 (cond ((not (or vi-1 vi-2)) 1034 construct-1) 1035 ((not vi-1) 1036 construct-2) 1037 ((not vi-2) 1038 construct-1) 1039 ((<= (start-revision vi-1) (start-revision vi-2)) 1040 construct-1) 1041 (t 1042 construct-2)))) 1043 1044 1045 (defgeneric VersionedConstructC-p (class-symbol) 1046 (:documentation "Returns t if the passed class is equal to VersionedConstructC 1047 or one of its subtypes.") 1048 (:method ((class-symbol symbol)) 1049 (or (eql class-symbol 'VersionedconstructC) 1050 (TopicC-p class-symbol) 1051 (TopicMapC-p class-symbol) 1052 (AssociationC-p class-symbol)))) 1053 1054 1055 (defmethod delete-construct :before ((construct VersionedConstructC)) 1056 (dolist (version-info (versions construct)) 1057 (delete-construct version-info))) 1058 1059 1060 (defmethod find-item-by-revision ((construct VersionedConstructC) 1061 (revision integer) &optional parent-construct) 1062 (declare (ignorable parent-construct)) 1063 (cond ((= revision 0) 1064 (find-most-recent-revision construct)) 1065 (t 1066 (when (find-if 1067 #'(lambda(vi) 1068 (and (>= revision (start-revision vi)) 1069 (or (< revision (end-revision vi)) 1070 (= 0 (end-revision vi))))) 1071 (versions construct)) 1072 construct)))) 1073 1074 1075 (defmethod get-most-recent-version-info ((construct VersionedConstructC)) 514 1076 (let ((result (find 0 (versions construct) :key #'end-revision))) 515 1077 (if result … … 521 1083 (first sorted-list)))))) ;latest version-info of marked-as-deleted constructs -> highest integer 522 1084 523 (defgeneric equivalent-constructs (construct1 construct2) 524 (:documentation "checks if two topic map constructs are equal according to the TMDM equality rules")) 525 526 (defgeneric strictly-equivalent-constructs (construct1 construct2) 527 (:documentation "checks if two topic map constructs are not identical but equal according to the TMDM equality rules") 528 (:method ((construct1 TopicMapConstructC) (construct2 TopicMapConstructC)) 529 (and (equivalent-constructs construct1 construct2) 530 (not (eq construct1 construct2))))) 531 532 (defgeneric internal-id (construct) 533 (:documentation "returns the internal id that uniquely identifies a 534 construct (currently simply its OID)")) 535 536 (defmethod internal-id ((construct TopicMapConstructC)) 537 (slot-value construct (find-symbol "OID" 'elephant))) 538 539 540 ;;;;;;;;;;;;;; 541 ;; 542 ;; TopicIdentificationC 543 544 (elephant:defpclass TopicIdentificationC (PointerC) 545 ((xtm-id 546 :accessor xtm-id 547 :type string 548 :initarg :xtm-id 549 :index t 550 :documentation "ID of the TM this identification came from")) 551 (:documentation "Identify topic items through generalized 552 topicids. A topic may have many original topicids, the class 553 representing one of them") ) 554 555 (defmethod find-all-equivalent ((construct TopicIdentificationC)) 556 (delete (xtm-id construct) (call-next-method) :key #'xtm-id :test #'string=)) 557 558 (defun init-topic-identification (top id xtm-id &key (revision *TM-REVISION*)) 559 "create a TopicIdentification object (if necessary) and initialize it with the 560 combination of the current topicid and the ID of the current XTM id" 561 ;(declare (TopicC top)) 562 (declare (string id)) 563 564 (flet ;prevent unnecessary copies of TopicIdentificationC objects 565 ((has-topic-identifier (top uri xtm-id) 566 (remove-if-not 567 (lambda (ident) 568 (and (string= (uri ident) uri) 569 (string= (xtm-id ident) xtm-id))) 570 (topic-identifiers top)))) 571 (unless (has-topic-identifier top id xtm-id) 572 (let 573 ((ti 574 (make-instance 575 'TopicIdentificationC 576 :uri id 577 :xtm-id xtm-id 578 :identified-construct top 579 :start-revision revision))) 580 ;(add-to-version-history ti :start-revision revision) 581 ti)))) 582 583 (defun xtm-id-p (xtm-id) 584 "checks if a xtm-id has been used before" 585 (elephant:get-instance-by-value 'TopicIdentificationC 586 'xtm-id xtm-id)) 587 588 589 ;;;;;;;;;;;;;; 590 ;; 591 ;; PSI 592 593 (elephant:defpclass PersistentIdC (IdentifierC) 594 ((identified-construct :accessor identified-construct 595 :initarg :identified-construct 596 :associate TopicC)) 597 (:index t) 598 (:documentation "Represents a PSI")) 599 600 601 ;;;;;;;;;;;;;; 602 ;; 603 ;; ReifiableConstructC 604 605 (elephant:defpclass ReifiableConstructC (TopicMapConstructC) 606 ((item-identifiers 607 :associate (ItemIdentifierC identified-construct) 608 :inherit t 609 :documentation "Slot that realizes a 1 to N 610 relation between reifiable constructs and their 611 identifiers; pseudo-initarg is :item-identifiers. Is inherited by all reifiable constructs") 612 (reifier 613 :associate TopicC 614 :inherit t 615 :documentation "Represents a reifier association to a topic, i.e. 616 it stands for a 1:1 association between this class and TopicC")) 617 (:documentation "Reifiable constructs as per TMDM")) 618 619 620 (defgeneric reifier (construct &key revision) 621 (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) 622 (when (slot-boundp construct 'reifier) 623 (slot-value construct 'reifier)))) 624 625 (defgeneric (setf reifier) (topic TopicC) 626 (:method (topic (construct ReifiableConstructC)) 627 (setf (slot-value construct 'reifier) topic))) 628 ; (setf (reified topic) construct))) 629 630 (defgeneric item-identifiers (construct &key revision) 631 (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) 632 (filter-slot-value-by-revision construct 'item-identifiers :start-revision revision))) 633 634 (defmethod initialize-instance :around ((instance ReifiableConstructC) &key (item-identifiers nil) (reifier nil)) 635 "adds associations to these ids after the instance was initialized." 636 (declare (list item-identifiers)) 637 (call-next-method) 638 (dolist (id item-identifiers) 639 (declare (ItemIdentifierC id)) 640 (setf (identified-construct id) instance)) 641 (when reifier 642 (add-reifier instance reifier)) 643 ;(setf (reifier instance) reifier)) 644 instance) 645 646 (defmethod delete-construct :before ((construct ReifiableConstructC)) 647 (dolist (id (item-identifiers construct)) 648 (delete-construct id)) 649 (when (reifier construct) 650 (let ((reifier-topic (reifier construct))) 651 (remove-reifier construct) 652 (delete-construct reifier-topic)))) 653 654 (defgeneric item-identifiers-p (constr) 655 (:documentation "Test for the existence of item identifiers") 656 (:method ((construct ReifiableConstructC)) (slot-predicate construct 'item-identifiers))) 657 658 (defgeneric topicid (construct &optional xtm-id) 659 (:documentation "Return the ID of a construct")) 660 661 (defmethod revision ((constr ReifiableConstructC)) 662 (start-revision constr)) 663 664 (defgeneric (setf revision) (revision construct) 665 (:documentation "The corresponding setter method")) 666 667 (defmethod (setf revision) ((revision integer) (constr ReifiableConstructC)) 668 (setf (start-revision constr) revision)) 669 670 (defgeneric get-all-identifiers-of-construct (construct) 671 (:documentation "Get all identifiers that a given construct has")) 672 673 (defmethod get-all-identifiers-of-construct ((construct ReifiableConstructC)) 674 (item-identifiers construct)) 675 676 (defmethod check-for-duplicate-identifiers ((construct ReifiableConstructC)) 677 (dolist (id (get-all-identifiers-of-construct construct)) 678 (when (> (length 679 (union 680 (elephant:get-instances-by-value 'ItemIdentifierC 'uri (uri id)) 681 (union 682 (elephant:get-instances-by-value 'PersistentIdC 'uri (uri id)) 683 (elephant:get-instances-by-value 'SubjectLocatorC 'uri (uri id))))) 684 1) 685 (error 686 (make-condition 'duplicate-identifier-error 687 :message (format nil "Duplicate Identifier ~a has been found" (uri id)) 688 :uri (uri id)))))) 689 690 (defmethod copy-item-identifiers ((from-construct ReifiableConstructC) 691 (to-construct ReifiableConstructC)) 692 "Internal method to copy over item idenfiers from a construct to 693 another on. Returns the set of new identifiers" 694 (mapc 695 (lambda (identifier) 696 (setf (identified-construct identifier) 697 to-construct)) 698 (set-difference (item-identifiers from-construct) 699 (item-identifiers to-construct) 700 :key #'uri :test #'string=))) 701 702 ;;;;;;;;;;;;;; 703 ;; 704 ;; ScopableC 705 706 (elephant:defpclass ScopableC () 707 ((themes :accessor themes 708 :associate (TopicC used-as-theme) 709 :inherit t 710 :many-to-many t 711 :documentation "list of this scope's themes; pseudo-initarg is :themes"))) 712 713 (defmethod initialize-instance :around ((instance ScopableC) &key (themes nil)) 714 (declare (list themes)) 715 (call-next-method) 716 (dolist (theme themes) 717 (elephant:add-association instance 'themes theme)) 718 instance) 719 720 (defmethod delete-construct :before ((construct ScopableC)) 721 (dolist (theme (themes construct)) 722 (elephant:remove-association construct 'themes theme))) 723 724 725 ;;;;;;;;;;;;;; 726 ;; 727 ;; TypableC 728 729 (elephant:defpclass TypableC () 730 ((instance-of :accessor instance-of 731 :initarg :instance-of 732 :associate TopicC 733 :inherit t 734 :documentation "topic that this construct is an instance of"))) 735 736 (defmethod delete-construct :before ((construct TypableC)) 737 (when (instance-of-p construct) 738 (elephant:remove-association construct 'instance-of (instance-of construct)))) 739 740 (defgeneric instance-of-p (construct) 741 (:documentation "is the instance-of slot bound and not nil") 742 (:method ((construct TypableC)) (slot-predicate construct 'instance-of))) 743 744 745 ;; (defmethod equivalent-constructs ((scope1 ScopeC) (scope2 ScopeC)) 746 ;; "scopes are equal if their themes are equal" 747 ;; (let 748 ;; ((themes1 749 ;; (map 'list #'internal-id (themes scope1))) 750 ;; (themes2 751 ;; (map 'list #'internal-id (themes scope2)))) 752 ;; (not (set-exclusive-or themes1 themes2 :key #'internal-id)))) 753 754 ;;;;;;;;;;;;;; 755 ;; 756 ;; CharacteristicC 1085 1086 (defgeneric find-most-recent-revision (construct) 1087 (:documentation "Returns the latest version-info-object of the passed 1088 construct.") 1089 (:method ((construct VersionedConstructC)) 1090 (when (find 0 (versions construct) :key #'end-revision) 1091 construct))) 1092 1093 1094 (defun add-version-info(construct start-revision) 1095 "Adds 'construct' to the given version. 1096 If the construct is a VersionedConstructC add-to-version-history 1097 is called directly. Otherwise there is called a corresponding 1098 add-<whatever> method that adds recursively 'construct' to its 1099 parent and so on." 1100 (declare (type (or TopicMapConstructC VersionedConstructC) construct) 1101 (integer start-revision)) 1102 (cond ((typep construct 'VersionedConstructC) 1103 (add-to-version-history construct :start-revision start-revision)) 1104 ((typep construct 'VariantC) 1105 (let ((name (parent construct :revision start-revision))) 1106 (when name 1107 (add-variant name construct :revision start-revision) 1108 (let ((top (parent name :revision start-revision))) 1109 (when top 1110 (add-name top name :revision start-revision)))))) 1111 ((typep construct 'CharacteristicC) 1112 (let ((top (parent construct :revision start-revision))) 1113 (when top 1114 (add-characteristic top construct :revision start-revision)))) 1115 ((typep construct 'RoleC) 1116 (let ((assoc (parent construct :revision start-revision))) 1117 (when assoc 1118 (add-role assoc construct :revision start-revision)))))) 1119 1120 1121 (defgeneric add-to-version-history (construct &key start-revision end-revision) 1122 (:documentation "Adds version history to a versioned construct") 1123 (:method ((construct VersionedConstructC) 1124 &key (start-revision (error (make-missing-argument-condition "From add-to-version-history(): start revision must be present" 'start-revision 'add-to-version-history))) 1125 (end-revision 0)) 1126 (let ((eql-version-info 1127 (find-if #'(lambda(vi) 1128 (and (= (start-revision vi) start-revision) 1129 (= (end-revision vi) end-revision))) 1130 (versions construct)))) 1131 (if eql-version-info 1132 eql-version-info 1133 (let ((current-version-info 1134 (get-most-recent-version-info construct))) 1135 (cond 1136 ((and current-version-info 1137 (= (end-revision current-version-info) start-revision)) 1138 (setf (end-revision current-version-info) end-revision) 1139 current-version-info) 1140 ((and current-version-info 1141 (= (end-revision current-version-info) 0)) 1142 (setf (end-revision current-version-info) start-revision) 1143 (let ((vi (make-instance 'VersionInfoC 1144 :start-revision start-revision 1145 :end-revision end-revision))) 1146 (elephant:add-association vi 'versioned-construct construct))) 1147 (t 1148 (let ((vi (make-instance 'VersionInfoC 1149 :start-revision start-revision 1150 :end-revision end-revision))) 1151 (elephant:add-association vi 'versioned-construct construct))))))))) 1152 1153 1154 1155 (defmethod marked-as-deleted-p ((construct VersionedConstructC)) 1156 (unless (find-if #'(lambda(vi) 1157 (= (end-revision vi) 0)) 1158 (versions construct)) 1159 t)) 1160 1161 1162 (defmethod mark-as-deleted ((construct VersionedConstructC) 1163 &key source-locator revision) 1164 (declare (ignorable source-locator)) 1165 (let 1166 ((last-version ;the last active version 1167 (find 0 (versions construct) :key #'end-revision))) 1168 (if (and last-version 1169 (= (start-revision last-version) revision)) 1170 (progn 1171 (delete-construct last-version) 1172 (let ((sorted-versions 1173 (sort (versions construct) #'> :key #'end-revision))) 1174 (when sorted-versions 1175 (setf (end-revision (first sorted-versions)) revision)))) 1176 (when last-version 1177 (setf (end-revision last-version) revision))))) 1178 1179 1180 ;;; TopicMapconstructC 1181 (defgeneric strictly-equivalent-constructs (construct-1 construct-2 1182 &key revision) 1183 (:documentation "Checks if two topic map constructs are not identical but 1184 equal according to the TMDM equality rules.") 1185 (:method ((construct-1 TopicMapConstructC) (construct-2 TopicMapConstructC) 1186 &key (revision *TM-REVISION*)) 1187 (declare (integer revision)) 1188 (and (equivalent-constructs construct-1 construct-2 :revision revision) 1189 (not (eql construct-1 construct-2))))) 1190 1191 1192 (defmethod check-for-duplicate-identifiers ((construct TopicMapConstructC) 1193 &key revision) 1194 (declare (ignorable revision construct)) 1195 ;do nothing 1196 ) 1197 1198 1199 (defmethod get-all-characteristics ((parent-construct TopicC) 1200 (characteristic-symbol symbol)) 1201 (cond ((OccurrenceC-p characteristic-symbol) 1202 (map 'list #'characteristic (slot-p parent-construct 'occurrences))) 1203 ((NameC-p characteristic-symbol) 1204 (map 'list #'characteristic (slot-p parent-construct 'names))))) 1205 1206 1207 (defgeneric TopicMapConstructC-p (class-symbol) 1208 (:documentation "Returns t if the passed class is equal to TopicMapConstructC 1209 or one of its subtypes.") 1210 (:method ((class-symbol symbol)) 1211 (or (eql class-symbol 'TopicMapConstructC) 1212 (ReifiableConstructC-p class-symbol) 1213 (PointerC-p class-symbol)))) 1214 1215 1216 ;;; PointerC 1217 (defmethod versions ((construct PointerC)) 1218 "Returns all versions that are indirectly through all PointerAssocitiations 1219 bound to the passed pointer object." 1220 (loop for p-assoc in (slot-p construct 'identified-construct) 1221 append (versions p-assoc))) 1222 1223 1224 (defmethod mark-as-deleted ((construct PointerC) &key source-locator revision) 1225 "Marks the last active relation between a pointer and its parent construct 1226 as deleted." 1227 (declare (ignorable source-locator)) 1228 (let ((owner (identified-construct construct :revision 0))) 1229 (when owner 1230 (cond ((typep construct 'PersistentIdC) 1231 (private-delete-psi owner construct :revision revision)) 1232 ((typep construct 'SubjectLocatorC) 1233 (private-delete-locator owner construct :revision revision)) 1234 ((typep construct 'ItemIdentifierC) 1235 (private-delete-item-identifier owner construct :revision revision)) 1236 ((typep construct 'TopicIdentificationC) 1237 (private-delete-topic-identifier owner construct :revision revision)))))) 1238 1239 1240 (defmethod marked-as-deleted-p ((construct PointerC)) 1241 (unless (identified-construct construct :revision 0) 1242 t)) 1243 1244 1245 (defmethod find-oldest-construct ((construct-1 PointerC) (construct-2 PointerC)) 1246 (let ((vi-1 (find-version-info (slot-p construct-1 'identified-construct))) 1247 (vi-2 (find-version-info (slot-p construct-2 'identified-construct)))) 1248 (cond ((not (or vi-1 vi-2)) 1249 construct-1) 1250 ((not vi-1) 1251 construct-2) 1252 ((not vi-2) 1253 construct-1) 1254 ((<= (start-revision vi-1) (start-revision vi-2)) 1255 construct-1) 1256 (t 1257 construct-2)))) 1258 1259 1260 (defmethod equivalent-constructs ((construct-1 PointerC) (construct-2 PointerC) 1261 &key (revision nil)) 1262 (declare (ignorable revision)) 1263 (string= (uri construct-1) (uri construct-2))) 1264 1265 1266 (defgeneric PointerC-p (class-symbol) 1267 (:documentation "Returns t if the passed symbol corresponds to the class 1268 PointerC or one of its subclasses.") 1269 (:method ((class-symbol symbol)) 1270 (or (eql class-symbol 'PointerC) 1271 (IdentifierC-p class-symbol) 1272 (TopicIdentificationC-p class-symbol) 1273 (PersistentIdC-p class-symbol) 1274 (ItemIdentifierC-p class-symbol) 1275 (SubjectLocatorC-p class-symbol)))) 1276 1277 1278 (defmethod equivalent-construct ((construct PointerC) 1279 &key start-revision (uri "")) 1280 "All Pointers are equal if they have the same URI value." 1281 (declare (string uri) (ignorable start-revision)) 1282 (string= (uri construct) uri)) 1283 1284 1285 (defmethod find-item-by-revision ((construct PointerC) 1286 (revision integer) &optional parent-construct) 1287 (if parent-construct 1288 (let ((parent-assoc 1289 (let ((assocs 1290 (remove-if 1291 #'null 1292 (map 'list #'(lambda(assoc) 1293 (when (eql (parent-construct assoc) 1294 parent-construct) 1295 assoc)) 1296 (slot-p construct 'identified-construct))))) 1297 (when assocs 1298 (first assocs))))) 1299 (when parent-assoc 1300 (cond ((= revision 0) 1301 (find-most-recent-revision parent-assoc)) 1302 (t 1303 (when (find-if 1304 #'(lambda(vi) 1305 (and (>= revision (start-revision vi)) 1306 (or (< revision (end-revision vi)) 1307 (= 0 (end-revision vi))))) 1308 (versions parent-assoc)) 1309 construct))))) 1310 nil)) 1311 1312 1313 (defmethod delete-construct :before ((construct PointerC)) 1314 (dolist (p-assoc (slot-p construct 'identified-construct)) 1315 (delete-construct p-assoc))) 1316 1317 1318 (defmethod owned-p ((construct PointerC)) 1319 (when (slot-p construct 'identified-construct) 1320 t)) 1321 1322 1323 (defgeneric identified-construct (construct &key revision) 1324 (:documentation "Returns the identified-construct -> ReifiableConstructC or 1325 TopicC that corresponds with the passed revision.") 1326 (:method ((construct PointerC) &key (revision *TM-REVISION*)) 1327 (let ((assocs 1328 (map 'list #'parent-construct 1329 (filter-slot-value-by-revision construct 'identified-construct 1330 :start-revision revision)))) 1331 (when assocs ;result must be nil or a list with one item 1332 (first assocs))))) 1333 1334 1335 ;;; TopicIdentificationC 1336 (defmethod equivalent-constructs ((construct-1 TopicIdentificationC) 1337 (construct-2 TopicIdentificationC) 1338 &key (revision nil)) 1339 (declare (ignorable revision)) 1340 (and (call-next-method) 1341 (string= (xtm-id construct-1) (xtm-id construct-2)))) 757 1342 758 1343 759 (elephant:defpclass CharacteristicC (ReifiableConstructC ScopableC TypableC) 760 ((topic :accessor topic 761 :initarg :topic 762 :associate TopicC 763 :documentation "The topic that this characteristic belongs to") 764 (charvalue :accessor charvalue 765 :type string 766 :initarg :charvalue 767 :index t 768 :documentation "the value of the characteristic in the given scope")) 769 (:documentation "Scoped characteristic of a topic (meant to be used 770 as an abstract class)")) 771 772 (defgeneric CharacteristicC-p (object) 773 (:documentation "test if object is a of type CharacteristicC") 774 (:method ((object t)) nil) 775 (:method ((object CharacteristicC)) object)) 1344 1345 (defgeneric TopicIdentificationC-p (class-symbol) 1346 (:documentation "Returns t if the passed class symbol is equal 1347 to TopicIdentificationC.") 1348 (:method ((class-symbol symbol)) 1349 (eql class-symbol 'TopicIdentificationC))) 1350 1351 1352 (defmethod equivalent-construct ((construct TopicIdentificationC) 1353 &key start-revision (uri "") (xtm-id "")) 1354 "TopicIdentifiers are equal if teh URI and XTM-ID values are equal." 1355 (declare (string uri xtm-id)) 1356 (let ((equivalent-pointer (call-next-method 1357 construct :start-revision start-revision 1358 :uri uri))) 1359 (and equivalent-pointer 1360 (string= (xtm-id construct) xtm-id)))) 1361 1362 1363 ;;; IdentifierC 1364 (defgeneric IdentifierC-p (class-symbol) 1365 (:documentation "Returns t if the passed symbol is equal to IdentifierC 1366 or one of its sybtypes.") 1367 (:method ((class-symbol symbol)) 1368 (or (eql class-symbol 'IdentifierC) 1369 (PersistentIdC-p class-symbol) 1370 (SubjectLocatorC-p class-symbol) 1371 (ItemIdentifierC-p class-symbol)))) 1372 1373 1374 ;;; PersistentIdC 1375 (defgeneric PersistentIdC-p (class-symbol) 1376 (:documentation "Returns t if the passed symbol is equal to PersistentIdC.") 1377 (:method ((class-symbol symbol)) 1378 (eql class-symbol 'PersistentIdC))) 1379 1380 1381 ;;; ItemIdentifierC 1382 (defgeneric ItemIdentifierC-p (class-symbol) 1383 (:documentation "Returns t if the passed symbol is equal to ItemIdentifierC.") 1384 (:method ((class-symbol symbol)) 1385 (eql class-symbol 'ItemIdentifierC))) 1386 1387 ;;; SubjectLocatorC 1388 (defgeneric SubjectLocatorC-p (class-symbol) 1389 (:documentation "Returns t if the passed symbol is equal to SubjectLocatorC.") 1390 (:method ((class-symbol symbol)) 1391 (eql class-symbol 'SubjectLocatorC))) 1392 1393 1394 ;;; PointerAssociationC 1395 (defmethod delete-construct :before ((construct PointerAssociationC)) 1396 (delete-1-n-association construct 'identifier)) 1397 1398 1399 ;;; ItemIdAssociationC 1400 (defmethod delete-construct :before ((construct ItemIdAssociationC)) 1401 (delete-1-n-association construct 'parent-construct)) 1402 1403 1404 ;;; TopicIdAssociationC 1405 (defmethod delete-construct :before ((construct TopicIdAssociationC)) 1406 (delete-1-n-association construct 'parent-construct)) 1407 1408 1409 ;;; PersistentIdAssociationC 1410 (defmethod delete-construct :before ((construct PersistentIdAssociationC)) 1411 (delete-1-n-association construct 'parent-construct)) 1412 1413 1414 ;;; SubjectLocatorAssociationC 1415 (defmethod delete-construct :before ((construct SubjectLocatorAssociationC)) 1416 (delete-1-n-association construct 'parent-construct)) 1417 1418 1419 ;;; ReifierAssociationC 1420 (defmethod delete-construct :before ((construct ReifierAssociationC)) 1421 (delete-1-n-association construct 'reifiable-construct) 1422 (delete-1-n-association construct 'reifier-topic)) 1423 1424 1425 ;;; TypeAssociationC 1426 (defmethod delete-construct :before ((construct TypeAssociationC)) 1427 (delete-1-n-association construct 'type-topic) 1428 (delete-1-n-association construct 'typable-construct)) 1429 1430 1431 ;;; ScopeAssociationC 1432 (defmethod delete-construct :before ((construct ScopeAssociationC)) 1433 (delete-1-n-association construct 'theme-topic) 1434 (delete-1-n-association construct 'scopable-construct)) 1435 1436 1437 ;;; CharacteristicAssociationC 1438 (defmethod delete-construct :before ((construct CharacteristicAssociationC)) 1439 (delete-1-n-association construct 'characteristic)) 1440 1441 1442 ;;; OccurrenceAssociationC 1443 (defmethod delete-construct :before ((construct OccurrenceAssociationC)) 1444 (delete-1-n-association construct 'parent-construct)) 1445 1446 1447 ;;; NameAssociationC 1448 (defmethod delete-construct :before ((construct NameAssociationC)) 1449 (delete-1-n-association construct 'parent-construct)) 1450 1451 1452 ;;; VariantAssociationC 1453 (defmethod delete-construct :before ((construct VariantAssociationC)) 1454 (delete-1-n-association construct 'parent-construct)) 1455 1456 1457 ;;; RoleAssociationC 1458 (defmethod delete-construct :before ((construct RoleAssociationC)) 1459 (delete-1-n-association construct 'role) 1460 (delete-1-n-association construct 'parent-construct)) 1461 1462 1463 ;;; PlayerAssociationC 1464 (defmethod delete-construct :before ((construct PlayerAssociationC)) 1465 (delete-1-n-association construct 'player-topic) 1466 (delete-1-n-association construct 'parent-construct)) 1467 1468 1469 ;;; TopicC 1470 (defmethod mark-as-deleted :around ((top TopicC) 1471 &key (source-locator nil sl-provided-p) 1472 revision) 1473 "Mark a topic as deleted if it comes from the source indicated by 1474 source-locator" 1475 ;;Part 1b, 1.4.3.3.1: 1476 ;; Let SP be the value of the ServerSourceLocatorPrefix element in the ATOM feed F 1477 ;; * Let SI be the value of TopicSI element in ATOM entry E 1478 ;; * feed F contains E) 1479 ;; * entry E references topic fragment TF 1480 ;; * Let LTM be the local topic map 1481 ;; * Let T be the topic in LTM that has a subjectidentifier that matches SI 1482 ;; * For all names, occurrences and associations in which T plays a role, TMC 1483 ;; * Delete all SrcLocators of TMC that begin with SP. If the count of srclocators on TMC = 0 then delete TMC 1484 ;; * Merge in the fragment TF using SP as the base all generated source locators. 1485 (when (or (and (not source-locator) sl-provided-p) 1486 (and sl-provided-p 1487 (some (lambda (psi) (string-starts-with (uri psi) source-locator)) 1488 (psis top :revision 0)))) 1489 (unless sl-provided-p 1490 (mapc (lambda(psi)(mark-as-deleted psi :revision revision 1491 :source-locator source-locator)) 1492 (psis top :revision 0))) 1493 (mapc (lambda(sl)(mark-as-deleted sl :revision revision 1494 :source-locator source-locator)) 1495 (locators top :revision 0)) 1496 (mapc (lambda (name) (mark-as-deleted name :revision revision 1497 :source-locator source-locator)) 1498 (names top :revision 0)) 1499 (mapc (lambda (occ) (mark-as-deleted occ :revision revision 1500 :source-locator source-locator)) 1501 (occurrences top :revision 0)) 1502 (mapc (lambda (ass) (mark-as-deleted ass :revision revision 1503 :source-locator source-locator)) 1504 (find-all-associations top :revision 0)) 1505 (call-next-method))) 1506 1507 1508 (defmethod equivalent-constructs ((construct-1 TopicC) (construct-2 TopicC) 1509 &key (revision *TM-REVISION*)) 1510 (declare (integer revision)) 1511 (let ((ids-1 (union (union (item-identifiers construct-1 :revision revision) 1512 (locators construct-1 :revision revision)) 1513 (psis construct-1 :revision revision))) 1514 (ids-2 (union (union (item-identifiers construct-2 :revision revision) 1515 (locators construct-2 :revision revision)) 1516 (psis construct-2 :revision revision)))) 1517 (when (intersection ids-1 ids-2) 1518 t))) 1519 1520 1521 (defgeneric TopicC-p (class-symbol) 1522 (:documentation "Returns t if the passed symbol is equal to TopicC.") 1523 (:method ((class-symbol symbol)) 1524 (eql class-symbol 'TopicC))) 1525 1526 1527 (defmethod equivalent-construct ((construct TopicC) 1528 &key (start-revision *TM-REVISION*) (psis nil) 1529 (locators nil) (item-identifiers nil) 1530 (topic-identifiers nil)) 1531 "Isidorus handles Topic-equality only by the topic's identifiers 1532 'psis', 'subject locators' and 'item identifiers'. Names and occurences 1533 are not checked becuase we don't know when a topic is finalized and owns 1534 all its charactersitics. T is returned if the topic owns one of the given 1535 identifier-URIs." 1536 (declare (integer start-revision) (list psis locators item-identifiers 1537 topic-identifiers)) 1538 (when 1539 (intersection 1540 (union (union (psis construct :revision start-revision) 1541 (locators construct :revision start-revision)) 1542 (union (item-identifiers construct :revision start-revision) 1543 (topic-identifiers construct :revision start-revision))) 1544 (union (union psis locators) (union item-identifiers topic-identifiers))) 1545 t)) 1546 1547 1548 (defmethod delete-construct :before ((construct TopicC)) 1549 (let ((psi-assocs-to-delete (slot-p construct 'psis)) 1550 (sl-assocs-to-delete (slot-p construct 'locators)) 1551 (name-assocs-to-delete (slot-p construct 'names)) 1552 (occ-assocs-to-delete (slot-p construct 'occurrences)) 1553 (role-assocs-to-delete (slot-p construct 'player-in-roles)) 1554 (type-assocs-to-delete (slot-p construct 'used-as-type)) 1555 (scope-assocs-to-delete (slot-p construct 'used-as-theme)) 1556 (reifier-assocs-to-delete (slot-p construct 'reified-construct))) 1557 (let ((all-psis (map 'list #'identifier psi-assocs-to-delete)) 1558 (all-sls (map 'list #'identifier sl-assocs-to-delete)) 1559 (all-names (map 'list #'characteristic name-assocs-to-delete)) 1560 (all-occs (map 'list #'characteristic occ-assocs-to-delete)) 1561 (all-roles (map 'list #'parent-construct role-assocs-to-delete)) 1562 (all-types (map 'list #'typable-construct type-assocs-to-delete))) 1563 (dolist (construct-to-delete (append psi-assocs-to-delete 1564 sl-assocs-to-delete 1565 name-assocs-to-delete 1566 occ-assocs-to-delete 1567 role-assocs-to-delete 1568 type-assocs-to-delete 1569 scope-assocs-to-delete 1570 reifier-assocs-to-delete)) 1571 (delete-construct construct-to-delete)) 1572 (dolist (candidate-to-delete (append all-psis all-sls all-names all-occs)) 1573 (unless (owned-p candidate-to-delete) 1574 (delete-construct candidate-to-delete))) 1575 (dolist (candidate-to-delete all-roles) 1576 (unless (player-p candidate-to-delete) 1577 (delete-construct candidate-to-delete))) 1578 (dolist (candidate-to-delete all-types) 1579 (unless (instance-of-p candidate-to-delete) 1580 (delete-construct candidate-to-delete))) 1581 (dolist (tm (slot-p construct 'in-topicmaps)) 1582 (remove-association construct 'in-topicmaps tm))))) 1583 1584 1585 (defmethod owned-p ((construct TopicC)) 1586 (when (slot-p construct 'in-topicmaps) 1587 t)) 1588 1589 1590 (defgeneric topic-id (construct &optional revision xtm-id) 1591 (:documentation "Returns the primary id of this item 1592 (= essentially the OID). If xtm-id is explicitly given, 1593 returns one of the topic-ids in that TM 1594 (which must then exist).") 1595 (:method ((construct TopicC) &optional (revision *TM-REVISION*) (xtm-id nil)) 1596 (declare (type (or string null) xtm-id) 1597 (type (or integer null) revision)) 1598 (if xtm-id 1599 (let ((possible-identifiers 1600 (remove-if-not 1601 #'(lambda(top-id) 1602 (string= (xtm-id top-id) xtm-id)) 1603 (topic-identifiers construct :revision revision)))) 1604 (unless possible-identifiers 1605 (error (make-object-not-found-condition (format nil "Could not find an object ~a in xtm-id ~a" construct xtm-id)))) 1606 (uri (first possible-identifiers))) 1607 (concatenate 'string "t" (write-to-string (internal-id construct)))))) 1608 1609 1610 (defgeneric topic-identifiers (construct &key revision) 1611 (:documentation "Returns the TopicIdentificationC-objects that correspond 1612 with the passed construct and the passed version.") 1613 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1614 (let ((assocs (filter-slot-value-by-revision 1615 construct 'topic-identifiers :start-revision revision))) 1616 (map 'list #'identifier assocs)))) 1617 1618 1619 (defgeneric add-topic-identifier (construct topic-identifier &key revision) 1620 (:documentation "Adds the passed topic-identifier to the passed topic. 1621 If the topic-identifier is already related with the passed 1622 topic a new revision is added. 1623 If the passed identifer already identifies another object 1624 the identified-constructs are merged.") 1625 (:method ((construct TopicC) (topic-identifier TopicIdentificationC) 1626 &key (revision *TM-REVISION*)) 1627 (let ((all-ids 1628 (map 'list #'identifier (slot-p construct 'topic-identifiers))) 1629 (construct-to-be-merged 1630 (let ((id-owner (identified-construct topic-identifier 1631 :revision revision))) 1632 (when (not (eql id-owner construct)) 1633 id-owner)))) 1634 (let ((merged-construct construct)) 1635 (cond (construct-to-be-merged 1636 (setf merged-construct 1637 (merge-constructs construct construct-to-be-merged 1638 :revision revision))) 1639 ((find topic-identifier all-ids) 1640 (let ((ti-assoc (loop for ti-assoc in (slot-p construct 1641 'topic-identifiers) 1642 when (eql (identifier ti-assoc) 1643 topic-identifier) 1644 return ti-assoc))) 1645 (add-to-version-history ti-assoc :start-revision revision))) 1646 (t 1647 (make-construct 'TopicIdAssociationC 1648 :parent-construct construct 1649 :identifier topic-identifier 1650 :start-revision revision))) 1651 (add-to-version-history merged-construct :start-revision revision) 1652 merged-construct)))) 1653 1654 1655 (defgeneric private-delete-topic-identifier 1656 (construct topic-identifier &key revision) 1657 (:documentation "Sets the association object between the passed constructs 1658 as mark-as-deleted.") 1659 (:method ((construct TopicC) (topic-identifier TopicIdentificationC) 1660 &key (revision (error (make-missing-argument-condition "From private-delete-topic-identifier(): revision must be set" 'revision 'private-delete-topic-identifier)))) 1661 (let ((assoc-to-delete (loop for ti-assoc in (slot-p construct 'topic-identifiers) 1662 when (eql (identifier ti-assoc) topic-identifier) 1663 return ti-assoc))) 1664 (when assoc-to-delete 1665 (mark-as-deleted assoc-to-delete :revision revision) 1666 construct)))) 1667 1668 1669 (defgeneric delete-topic-identifier 1670 (construct topic-identifier &key revision) 1671 (:documentation "See private-delete-topic-identifier but adds the parent 1672 construct to the given version") 1673 (:method ((construct TopicC) (topic-identifier TopicIdentificationC) 1674 &key (revision (error (make-missing-argument-condition "From delete-topic-identifier(): revision must be set" 'revision 'delete-topic-identifier)))) 1675 (when (private-delete-topic-identifier construct topic-identifier 1676 :revision revision) 1677 (add-to-version-history construct :start-revision revision) 1678 construct))) 1679 1680 1681 (defgeneric psis (construct &key revision) 1682 (:documentation "Returns the PersistentIdC-objects that correspond 1683 with the passed construct and the passed version.") 1684 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1685 (let ((assocs (filter-slot-value-by-revision 1686 construct 'psis :start-revision revision))) 1687 (map 'list #'identifier assocs)))) 1688 1689 1690 (defgeneric add-psi (construct psi &key revision) 1691 (:documentation "Adds the passed psi to the passed topic. 1692 If the psi is already related with the passed 1693 topic a new revision is added. 1694 If the passed identifer already identifies another object 1695 the identified-constructs are merged.") 1696 (:method ((construct TopicC) (psi PersistentIdC) 1697 &key (revision *TM-REVISION*)) 1698 (let ((all-ids 1699 (map 'list #'identifier (slot-p construct 'psis))) 1700 (construct-to-be-merged 1701 (let ((id-owner (identified-construct psi :revision revision))) 1702 (when (not (eql id-owner construct)) 1703 id-owner)))) 1704 (let ((merged-construct construct)) 1705 (cond (construct-to-be-merged 1706 (setf merged-construct 1707 (merge-constructs construct construct-to-be-merged 1708 :revision revision))) 1709 ((find psi all-ids) 1710 (let ((psi-assoc (loop for psi-assoc in (slot-p construct 'psis) 1711 when (eql (identifier psi-assoc) psi) 1712 return psi-assoc))) 1713 (add-to-version-history psi-assoc :start-revision revision))) 1714 (t 1715 (make-construct 'PersistentIdAssociationC 1716 :parent-construct construct 1717 :identifier psi 1718 :start-revision revision))) 1719 (add-to-version-history merged-construct :start-revision revision) 1720 merged-construct)))) 1721 1722 1723 (defgeneric private-delete-psi (construct psi &key revision) 1724 (:documentation "Sets the association object between the passed constructs 1725 as mark-as-deleted.") 1726 (:method ((construct TopicC) (psi PersistentIdC) 1727 &key (revision (error (make-missing-argument-condition "From private-delete-psi(): revision must be set" 'revision 'private-delete-psi)))) 1728 (let ((assoc-to-delete (loop for psi-assoc in (slot-p construct 'psis) 1729 when (eql (identifier psi-assoc) psi) 1730 return psi-assoc))) 1731 (when assoc-to-delete 1732 (mark-as-deleted assoc-to-delete :revision revision) 1733 construct)))) 1734 1735 1736 (defgeneric delete-psi (construct psi &key revision) 1737 (:documentation "See private-delete-psis but adds the parent to the given 1738 version.") 1739 (:method ((construct TopicC) (psi PersistentIdC) 1740 &key (revision (error (make-missing-argument-condition "From delete-psi(): revision must be set" 'revision 'delete-psi)))) 1741 (when (private-delete-psi construct psi :revision revision) 1742 (add-to-version-history construct :start-revision revision) 1743 construct))) 1744 1745 1746 (defgeneric locators (construct &key revision) 1747 (:documentation "Returns the SubjectLocatorC-objects that correspond 1748 with the passed construct and the passed version.") 1749 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1750 (let ((assocs (filter-slot-value-by-revision 1751 construct 'locators :start-revision revision))) 1752 (map 'list #'identifier assocs)))) 1753 1754 1755 (defgeneric add-locator (construct locator &key revision) 1756 (:documentation "Adds the passed locator to the passed topic. 1757 If the locator is already related with the passed 1758 topic a new revision is added. 1759 If the passed identifer already identifies another object 1760 the identified-constructs are merged.") 1761 (:method ((construct TopicC) (locator SubjectLocatorC) 1762 &key (revision *TM-REVISION*)) 1763 (let ((all-ids 1764 (map 'list #'identifier (slot-p construct 'locators))) 1765 (construct-to-be-merged 1766 (let ((id-owner (identified-construct locator :revision revision))) 1767 (when (not (eql id-owner construct)) 1768 id-owner)))) 1769 (let ((merged-construct construct)) 1770 (cond (construct-to-be-merged 1771 (setf merged-construct 1772 (merge-constructs construct construct-to-be-merged 1773 :revision revision))) 1774 ((find locator all-ids) 1775 (let ((loc-assoc 1776 (loop for loc-assoc in (slot-p construct 'locators) 1777 when (eql (identifier loc-assoc) locator) 1778 return loc-assoc))) 1779 (add-to-version-history loc-assoc :start-revision revision))) 1780 (t 1781 (make-construct 'SubjectLocatorAssociationC 1782 :parent-construct construct 1783 :identifier locator 1784 :start-revision revision))) 1785 (add-to-version-history merged-construct :start-revision revision) 1786 merged-construct)))) 1787 1788 1789 (defgeneric private-delete-locator (construct locator &key revision) 1790 (:documentation "Sets the association object between the passed constructs 1791 as mark-as-deleted.") 1792 (:method ((construct TopicC) (locator SubjectLocatorC) 1793 &key (revision (error (make-missing-argument-condition "From private-delete-locator(): revision must be set" 'revision 'private-delete-locator)))) 1794 (let ((assoc-to-delete (loop for loc-assoc in (slot-p construct 'locators) 1795 when (eql (identifier loc-assoc) locator) 1796 return loc-assoc))) 1797 (when assoc-to-delete 1798 (mark-as-deleted assoc-to-delete :revision revision) 1799 construct)))) 1800 1801 1802 (defgeneric delete-locator (construct locator &key revision) 1803 (:documentation "See private-delete-locator but add the parent construct 1804 to the given version.") 1805 (:method ((construct TopicC) (locator SubjectLocatorC) 1806 &key (revision (error (make-missing-argument-condition "From delete-locator(): revision must be set" 'revision 'delete-locator)))) 1807 (when (private-delete-locator construct locator :revision revision) 1808 (add-to-version-history construct :start-revision revision) 1809 construct))) 1810 1811 1812 (defmethod get-all-identifiers-of-construct ((construct TopicC) 1813 &key (revision *TM-REVISION*)) 1814 (declare (integer revision)) 1815 (append (psis construct :revision revision) 1816 (locators construct :revision revision) 1817 (item-identifiers construct :revision revision))) 1818 1819 1820 (defgeneric names (construct &key revision) 1821 (:documentation "Returns the NameC-objects that correspond 1822 with the passed construct and the passed version.") 1823 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1824 (let ((assocs (filter-slot-value-by-revision 1825 construct 'names :start-revision revision))) 1826 (map 'list #'characteristic assocs)))) 1827 1828 1829 (defgeneric add-name (construct name &key revision) 1830 (:documentation "Adds the passed name to the passed topic. 1831 If the name is already related with the passed 1832 topic a new revision is added. 1833 If the passed name already owns another object 1834 an error is thrown.") 1835 (:method ((construct TopicC) (name NameC) 1836 &key (revision *TM-REVISION*)) 1837 (when (and (parent name :revision revision) 1838 (not (eql (parent name :revision revision) construct))) 1839 (error (make-tm-reference-condition (format nil "From add-name(): ~a can't be owned by ~a since it is already owned by the topic ~a" 1840 name construct (parent name :revision revision)) 1841 name (parent name :revision revision) construct))) 1842 (if (merge-if-equivalent name construct :revision revision) 1843 construct 1844 (let ((all-names 1845 (map 'list #'characteristic (slot-p construct 'names)))) 1846 (if (find name all-names) 1847 (let ((name-assoc 1848 (loop for name-assoc in (slot-p construct 'names) 1849 when (eql (parent-construct name-assoc) 1850 construct) 1851 return name-assoc))) 1852 (add-to-version-history name-assoc :start-revision revision)) 1853 (make-construct 'NameAssociationC 1854 :parent-construct construct 1855 :characteristic name 1856 :start-revision revision)) 1857 (add-to-version-history construct :start-revision revision) 1858 construct)))) 1859 1860 1861 (defgeneric private-delete-name (construct name &key revision) 1862 (:documentation "Sets the association object between the passed constructs 1863 as mark-as-deleted.") 1864 (:method ((construct TopicC) (name NameC) 1865 &key (revision (error (make-missing-argument-condition "From private-delete-name(): revision must be set" 'revision 'private-delete-name)))) 1866 (let ((assoc-to-delete (loop for name-assoc in (slot-p construct 'names) 1867 when (eql (characteristic name-assoc) name) 1868 return name-assoc))) 1869 (when assoc-to-delete 1870 (mark-as-deleted assoc-to-delete :revision revision) 1871 construct)))) 1872 1873 1874 (defgeneric delete-name (construct name &key revision) 1875 (:documentation "See private-delete-name but adds the parent to 1876 the given version.") 1877 (:method ((construct TopicC) (name NameC) 1878 &key (revision (error (make-missing-argument-condition "From delete-name(): revision must be set" 'revision 'delete-name)))) 1879 (when (private-delete-name construct name :revision revision) 1880 (add-to-version-history construct :start-revision revision) 1881 construct))) 1882 1883 1884 (defgeneric occurrences (construct &key revision) 1885 (:documentation "Returns the OccurrenceC-objects that correspond 1886 with the passed construct and the passed version.") 1887 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1888 (let ((assocs (filter-slot-value-by-revision 1889 construct 'occurrences :start-revision revision))) 1890 (map 'list #'characteristic assocs)))) 1891 1892 1893 (defgeneric add-occurrence (construct occurrence &key revision) 1894 (:documentation "Adds the passed occurrence to the passed topic. 1895 If the occurrence is already related with the passed 1896 topic a new revision is added. 1897 If the passed occurrence already owns another object 1898 an error is thrown.") 1899 (:method ((construct TopicC) (occurrence OccurrenceC) 1900 &key (revision *TM-REVISION*)) 1901 (when (and (parent occurrence :revision revision) 1902 (not (eql (parent occurrence :revision revision) construct))) 1903 (error (make-tm-reference-condition (format nil "From add-occurrence(): ~a can't be owned by ~a since it is already owned by the topic ~a" 1904 occurrence construct (parent occurrence :revision revision)) 1905 occurrence (parent occurrence :revision revision) construct))) 1906 (if (merge-if-equivalent occurrence construct :revision revision) 1907 construct 1908 (let ((all-occurrences 1909 (map 'list #'characteristic (slot-p construct 'occurrences)))) 1910 (if (find occurrence all-occurrences) 1911 (let ((occ-assoc 1912 (loop for occ-assoc in (slot-p construct 'occurrences) 1913 when (eql (parent-construct occ-assoc) construct) 1914 return occ-assoc))) 1915 (add-to-version-history occ-assoc :start-revision revision)) 1916 (make-construct 'OccurrenceAssociationC 1917 :parent-construct construct 1918 :characteristic occurrence 1919 :start-revision revision)) 1920 (add-to-version-history construct :start-revision revision) 1921 construct)))) 1922 1923 1924 (defgeneric private-delete-occurrence (construct occurrence &key revision) 1925 (:documentation "Sets the association object between the passed constructs 1926 as mark-as-deleted.") 1927 (:method ((construct TopicC) (occurrence OccurrenceC) 1928 &key (revision (error (make-missing-argument-condition "From private-delete-occurrence(): revision must be set" 'revision 'private-delete-occurrence)))) 1929 (let ((assoc-to-delete (loop for occ-assoc in (slot-p construct 'occurrences) 1930 when (eql (characteristic occ-assoc) occurrence) 1931 return occ-assoc))) 1932 (when assoc-to-delete 1933 (mark-as-deleted assoc-to-delete :revision revision) 1934 construct)))) 1935 1936 1937 (defgeneric delete-occurrence (construct occurrence &key revision) 1938 (:documentation "See private-delete-occurrence but adds the parent 1939 to the given version history.") 1940 (:method ((construct TopicC) (occurrence OccurrenceC) 1941 &key (revision (error (make-missing-argument-condition "From delete-occurrence(): revision must be set" 'revision 'delete-occurrence)))) 1942 (when (private-delete-occurrence construct occurrence :revision revision) 1943 (add-to-version-history construct :start-revision revision) 1944 construct))) 1945 1946 1947 (defmethod add-characteristic ((construct TopicC) 1948 (characteristic CharacteristicC) 1949 &key (revision *TM-REVISION*)) 1950 (declare (integer revision) (type (or NameC OccurrenceC) characteristic)) 1951 (if (typep characteristic 'NameC) 1952 (add-name construct characteristic :revision revision) 1953 (add-occurrence construct characteristic :revision revision))) 1954 1955 1956 (defmethod private-delete-characteristic ((construct TopicC) 1957 (characteristic CharacteristicC) 1958 &key (revision (error (make-missing-argument-condition "From private-delete-characteristic(): revision must be set" 'revision 'private-delete-characteristic)))) 1959 (declare (integer revision) (type (or NameC OccurrenceC) characteristic)) 1960 (if (typep characteristic 'NameC) 1961 (private-delete-name construct characteristic :revision revision) 1962 (private-delete-occurrence construct characteristic 1963 :revision revision))) 1964 1965 1966 (defmethod delete-characteristic ((construct TopicC) 1967 (characteristic CharacteristicC) 1968 &key (revision (error (make-missing-argument-condition "From delete-characteristic(): revision must be set" 'revision 'delete-characteristic)))) 1969 (declare (integer revision) (type (or NameC OccurrenceC) characteristic)) 1970 (if (typep characteristic 'NameC) 1971 (delete-name construct characteristic :revision revision) 1972 (delete-occurrence construct characteristic :revision revision))) 1973 1974 1975 (defgeneric player-in-roles (construct &key revision) 1976 (:documentation "Returns the RoleC-objects that correspond 1977 with the passed construct and the passed version.") 1978 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1979 (let ((assocs (filter-slot-value-by-revision 1980 construct 'player-in-roles :start-revision revision))) 1981 (map 'list #'parent-construct assocs)))) 1982 1983 1984 (defgeneric used-as-type (construct &key revision) 1985 (:documentation "Returns the TypableC-objects that correspond 1986 with the passed construct and the passed version.") 1987 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1988 (let ((assocs (filter-slot-value-by-revision 1989 construct 'used-as-type :start-revision revision))) 1990 (map 'list #'typable-construct assocs)))) 1991 1992 1993 (defgeneric used-as-theme (construct &key revision) 1994 (:documentation "Returns the ScopableC-objects that correspond 1995 with the passed construct and the passed version.") 1996 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 1997 (let ((assocs (filter-slot-value-by-revision 1998 construct 'used-as-theme :start-revision revision))) 1999 (map 'list #'scopable-construct assocs)))) 2000 2001 2002 (defgeneric reified-construct (construct &key revision) 2003 (:documentation "Returns the ReifiableConstructC-objects that correspond 2004 with the passed construct and the passed version.") 2005 (:method ((construct TopicC) &key (revision *TM-REVISION*)) 2006 (let ((assocs (filter-slot-value-by-revision 2007 construct 'reified-construct :start-revision revision))) 2008 (when assocs 2009 (reifiable-construct (first assocs)))))) 2010 2011 2012 (defgeneric add-reified-construct (construct reified-construct &key revision) 2013 (:documentation "Sets the passed construct as reified-consturct of the given 2014 topic.") 2015 (:method ((construct TopicC) (reified-construct ReifiableConstructC) 2016 &key (revision *TM-REVISION*)) 2017 (declare (integer revision)) 2018 (add-reifier reified-construct construct :revision revision))) 2019 2020 2021 (defgeneric private-delete-reified-construct 2022 (construct reified-construct &key revision) 2023 (:documentation "Unsets the passed construct as reified-construct of the 2024 given topic.") 2025 (:method ((construct TopicC) (reified-construct ReifiableConstructC) 2026 &key (revision (error (make-missing-argument-condition "From private-delete-reified-construct(): revision must be set" 'revision 'private-delete-reified-construct)))) 2027 (declare (integer revision)) 2028 (private-delete-reifier reified-construct construct 2029 :revision revision))) 2030 2031 2032 (defgeneric delete-reified-construct (construct reified-construct &key revision) 2033 (:documentation "See private-delete-reified-construct but adds the 2034 reifier to the given version.") 2035 (:method ((construct TopicC) (reified-construct ReifiableConstructC) 2036 &key (revision (error (make-missing-argument-condition "From -delete-reified-construct(): revision must be set" 'revision '-delete-reified-construct)))) 2037 (declare (integer revision)) 2038 (delete-reifier reified-construct construct :revision revision))) 2039 2040 2041 (defmethod in-topicmaps ((topic TopicC) &key (revision *TM-REVISION*)) 2042 (filter-slot-value-by-revision topic 'in-topicmaps :start-revision revision)) 2043 2044 2045 (defun get-item-by-id (topic-id &key (xtm-id *CURRENT-XTM*) 2046 (revision *TM-REVISION*) (error-if-nil nil)) 2047 "Gets a topic by its id, assuming an xtm-id. If xtm-id is empty, the current TM 2048 is chosen. If xtm-id is nil, choose the global TM with its internal ID, if 2049 applicable in the correct revision. If revison is provided, then the code checks 2050 if the topic already existed in this revision and returns nil otherwise. 2051 If no item meeting the constraints was found, then the return value is either 2052 NIL or an error is thrown, depending on error-if-nil." 2053 (declare (string topic-id) (integer revision)) 2054 (let ((result 2055 (if xtm-id 2056 (let ((possible-top-ids 2057 (delete-if-not 2058 #'(lambda(top-id) 2059 (and (typep top-id 'd:TopicIdentificationC) 2060 ;fixes a bug in elephant -> all PointerCs are returned 2061 (string= (xtm-id top-id) xtm-id) 2062 (string= (uri top-id) topic-id))) 2063 ;fixes a bug in get-instances-by-value that does a 2064 ;case-insensitive comparision 2065 (elephant:get-instances-by-value 2066 'TopicIdentificationC 2067 'uri topic-id)))) 2068 (when (and possible-top-ids 2069 (identified-construct (first possible-top-ids) 2070 :revision revision)) 2071 (unless (= (length possible-top-ids) 1) 2072 (error (make-duplicate-identifier-condition 2073 (format nil "(length possible-items ~a) for id ~a and xtm-id ~a > 1" 2074 possible-top-ids topic-id xtm-id) 2075 topic-id))) 2076 (identified-construct (first possible-top-ids) 2077 :revision revision) 2078 ;no revision need not to be checked, since the revision 2079 ;is implicitely checked by the function identified-construct 2080 )) 2081 (when (and (> (length topic-id) 0) 2082 (eql (elt topic-id 0) #\t) 2083 (string-integer-p (subseq topic-id 1))) 2084 (let ((top-from-oid 2085 (elephant::controller-recreate-instance 2086 elephant::*store-controller* 2087 (parse-integer (subseq topic-id 1))))) 2088 (when (find-item-by-revision top-from-oid revision) 2089 top-from-oid)))))) 2090 (if (and error-if-nil (not result)) 2091 (error (make-object-not-found-condition (format nil "No such item (id: ~a, tm: ~a, rev: ~a)" topic-id xtm-id revision))) 2092 result))) 2093 2094 2095 (defun get-item-by-identifier (uri &key (revision *TM-REVISION*) 2096 (identifier-type-symbol 'PersistentIdC) 2097 (error-if-nil nil)) 2098 "Returns the construct that is bound to the given identifier-uri." 2099 (declare (string uri) (integer revision) (symbol identifier-type-symbol)) 2100 (let ((result 2101 (let ((possible-ids 2102 (delete-if-not 2103 #'(lambda(id) 2104 (and (typep id identifier-type-symbol) 2105 (string= (uri id) uri))) 2106 (get-instances-by-value identifier-type-symbol 'uri uri)))) 2107 (when (and possible-ids 2108 (identified-construct (first possible-ids) 2109 :revision revision)) 2110 (unless (= (length possible-ids) 1) 2111 (error (make-duplicate-identifier-condition (format nil "(length possible-items ~a) for id ~a" possible-ids uri) uri))) 2112 (identified-construct (first possible-ids) 2113 :revision revision))))) 2114 ;no revision need to be checked, since the revision 2115 ;is implicitely checked by the function identified-construct 2116 (if (and result 2117 (let ((parent-elem 2118 (when (or (typep result 'CharacteristicC) 2119 (typep result 'RoleC)) 2120 (parent result :revision revision)))) 2121 (find-item-by-revision result revision parent-elem))) 2122 result 2123 (when error-if-nil 2124 (error (make-object-not-found-condition "No such item is bound to the given identifier uri.")))))) 2125 2126 2127 (defun get-item-by-item-identifier (uri &key (revision *TM-REVISION*) 2128 (error-if-nil nil)) 2129 "Returns a ReifiableConstructC that is bound to the identifier-uri." 2130 (get-item-by-identifier uri :revision revision 2131 :identifier-type-symbol 'ItemIdentifierC 2132 :error-if-nil error-if-nil)) 2133 2134 2135 (defun get-item-by-psi (uri &key (revision *TM-REVISION*) (error-if-nil nil)) 2136 "Returns a TopicC that is bound to the identifier-uri." 2137 (get-item-by-identifier uri :revision revision 2138 :identifier-type-symbol 'PersistentIdC 2139 :error-if-nil error-if-nil)) 2140 2141 2142 (defun get-item-by-locator (uri &key (revision *TM-REVISION*) (error-if-nil nil)) 2143 "Returns a TopicC that is bound to the identifier-uri." 2144 (get-item-by-identifier uri :revision revision 2145 :identifier-type-symbol 'SubjectLocatorC 2146 :error-if-nil error-if-nil)) 2147 2148 2149 (defgeneric list-instanceOf (topic &key tm revision) 2150 (:documentation "Generates a list of all topics that this topic is an 2151 instance of, optionally filtered by a topic map") 2152 (:method ((topic TopicC) &key (tm nil) (revision *TM-REVISION*)) 2153 (declare (type (or null TopicMapC) tm) 2154 (integer revision)) 2155 (remove-if 2156 #'null 2157 (map 'list 2158 #'(lambda(x) 2159 (when (loop for psi in (psis (instance-of x :revision revision) 2160 :revision revision) 2161 when (string= (uri psi) constants:*instance-psi*) 2162 return t) 2163 (loop for role in (roles (parent x :revision revision) 2164 :revision revision) 2165 when (not (eq role x)) 2166 return (player role :revision revision)))) 2167 (if tm 2168 (remove-if-not 2169 (lambda (role) 2170 (in-topicmap tm (parent role :revision revision) 2171 :revision revision)) 2172 (player-in-roles topic :revision revision)) 2173 (player-in-roles topic :revision revision)))))) 2174 2175 2176 (defgeneric list-super-types (topic &key tm revision) 2177 (:documentation "Generate a list of all topics that this topic is an 2178 subclass of, optionally filtered by a topic map") 2179 (:method ((topic TopicC) &key (tm nil) (revision *TM-REVISION*)) 2180 (declare (type (or null TopicMapC) tm) 2181 (integer revision)) 2182 (remove-if 2183 #'null 2184 (map 'list 2185 #'(lambda(x) 2186 (when (loop for psi in (psis (instance-of x :revision revision) 2187 :revision revision) 2188 when (string= (uri psi) *subtype-psi*) 2189 return t) 2190 (loop for role in (roles (parent x :revision revision) 2191 :revision revision) 2192 when (not (eq role x)) 2193 return (player role :revision revision)))) 2194 (if tm 2195 (remove-if-not 2196 (lambda (role) 2197 (in-topicmap tm (parent role :revision revision) 2198 :revision revision)) 2199 (player-in-roles topic :revision revision)) 2200 (player-in-roles topic :revision revision)))))) 2201 2202 2203 ;;; CharacteristicC 2204 (defmethod versions ((construct CharacteristicC)) 2205 "Returns all versions that are indirectly through all 2206 CharacteristicAssocitiations bound to the passed characteristic object." 2207 (loop for p-assoc in (slot-p construct 'parent) 2208 append (versions p-assoc))) 2209 2210 2211 (defmethod mark-as-deleted ((construct CharacteristicC) &key source-locator revision) 2212 "Marks the last active relation between a characteristic and its parent topic 2213 as deleted." 2214 (declare (ignorable source-locator)) 2215 (let ((owner (parent construct :revision 0))) 2216 (when owner 2217 (private-delete-characteristic owner construct :revision revision)))) 2218 2219 2220 (defmethod marked-as-deleted-p ((construct CharacteristicC)) 2221 (unless (parent construct :revision 0) 2222 t)) 2223 2224 2225 (defmethod find-self-or-equal ((construct CharacteristicC) 2226 (parent-construct TopicC) 2227 &key (revision *TM-REVISION*)) 2228 (declare (integer revision) (type (or OccurrenceC NameC) construct)) 2229 (let ((chars (if (typep construct 'OccurrenceC) 2230 (occurrences parent-construct :revision revision) 2231 (names parent-construct :revision revision)))) 2232 (let ((self (find construct chars))) 2233 (if self 2234 self 2235 (let ((equal-char 2236 (remove-if #'null 2237 (map 'list 2238 #'(lambda(char) 2239 (strictly-equivalent-constructs 2240 char construct :revision revision)) 2241 chars)))) 2242 (when equal-char 2243 (first equal-char))))))) 2244 2245 2246 (defmethod delete-if-not-referenced ((construct CharacteristicC)) 2247 (let ((references (slot-p construct 'parent))) 2248 (when (or (not references) 2249 (and (= (length references) 1) 2250 (marked-as-deleted-p (first references)))) 2251 (delete-construct construct)))) 2252 2253 2254 (defmethod find-oldest-construct ((construct-1 CharacteristicC) 2255 (construct-2 CharacteristicC)) 2256 (let ((vi-1 (find-version-info (slot-p construct-1 'parent))) 2257 (vi-2 (find-version-info (slot-p construct-2 'parent)))) 2258 (cond ((not (or vi-1 vi-2)) 2259 construct-1) 2260 ((not vi-1) 2261 construct-2) 2262 ((not vi-2) 2263 construct-1) 2264 ((<= (start-revision vi-1) (start-revision vi-2)) 2265 construct-1) 2266 (t 2267 construct-2)))) 2268 2269 2270 (defmethod equivalent-constructs ((construct-1 CharacteristicC) 2271 (construct-2 CharacteristicC) 2272 &key (revision *TM-REVISION*)) 2273 (declare (integer revision)) 2274 (and (string= (charvalue construct-1) (charvalue construct-2)) 2275 (eql (instance-of construct-1 :revision revision) 2276 (instance-of construct-2 :revision revision)) 2277 (not (set-exclusive-or (themes construct-1 :revision revision) 2278 (themes construct-2 :revision revision))))) 2279 2280 2281 (defgeneric CharacteristicC-p (class-symbol) 2282 (:documentation "Returns t if the passed symbol is equal to CharacteristicC 2283 or one of its subtypes.") 2284 (:method ((class-symbol symbol)) 2285 (or (eql class-symbol 'CharacteristicC) 2286 (OccurrenceC-p class-symbol) 2287 (NameC-p class-symbol) 2288 (VariantC-p class-symbol)))) 2289 2290 2291 (defmethod equivalent-construct ((construct CharacteristicC) 2292 &key (start-revision *TM-REVISION*) 2293 (charvalue "") (instance-of nil) (themes nil)) 2294 "Equality rule: Characteristics are equal if charvalue, themes and 2295 instance-of are equal." 2296 (declare (string charvalue) (list themes) 2297 (integer start-revision) 2298 (type (or null TopicC) instance-of)) 2299 ;; item-identifiers and reifers are not checked because the equality have to 2300 ;; be variafied without them 2301 (and (string= (charvalue construct) charvalue) 2302 (equivalent-scopable-construct construct themes 2303 :start-revision start-revision) 2304 (equivalent-typable-construct construct instance-of 2305 :start-revision start-revision))) 2306 2307 2308 (defmethod find-item-by-revision ((construct CharacteristicC) 2309 (revision integer) &optional parent-construct) 2310 (if parent-construct 2311 (let ((parent-assoc 2312 (let ((assocs 2313 (remove-if 2314 #'null 2315 (map 'list #'(lambda(assoc) 2316 (when (eql (parent-construct assoc) 2317 parent-construct) 2318 assoc)) 2319 (slot-p construct 'parent))))) 2320 (when assocs 2321 (first assocs))))) 2322 (when parent-assoc 2323 (cond ((= revision 0) 2324 (when 2325 (find-most-recent-revision parent-assoc) 2326 construct)) 2327 (t 2328 (when (find-if 2329 #'(lambda(vi) 2330 (and (>= revision (start-revision vi)) 2331 (or (< revision (end-revision vi)) 2332 (= 0 (end-revision vi))))) 2333 (versions parent-assoc)) 2334 construct))))) 2335 nil)) 2336 776 2337 777 2338 (defmethod delete-construct :before ((construct CharacteristicC)) 778 (delete-1-n-association construct 'topic)) 779 780 (defun get-item-by-content (content &key (revision *TM-REVISION*)) 781 "Find characteristis by their (atomic) content" 782 (flet 783 ((get-existing-instances (classname) 784 (delete-if-not #'(lambda (constr) 785 (find-item-by-revision constr revision)) 786 (elephant:get-instances-by-value classname 'charvalue content)))) 787 (nconc (get-existing-instances 'OccurenceC) 788 (get-existing-instances 'NameC)))) 789 790 791 792 793 ;;;;;;;;;;;;;; 794 ;; 795 ;; VariantC 796 797 (elephant:defpclass VariantC (CharacteristicC) 798 ((datatype :accessor datatype 799 :initarg :datatype 800 :initform nil 801 :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)") 802 (name :accessor name 803 :initarg :name 804 :associate NameC 805 :documentation "references the NameC instance which is the owner of this element"))) 806 807 808 (defgeneric VariantC-p (object) 809 (:documentation "test if object is a of type VariantC") 810 (:method ((object t)) nil) 811 (:method ((object VariantC)) object)) 812 813 814 (defmethod delete-construct :before ((construct VariantC)) 815 (delete-1-n-association construct 'name)) 816 817 818 (defmethod find-all-equivalent ((construct VariantC)) 819 (let ((parent (and (slot-boundp construct 'name) 820 (name construct)))) 821 (when parent 822 (delete-if-not #'(lambda(x)(strictly-equivalent-constructs construct x)) 823 (slot-value parent 'variants))))) 824 825 826 (defmethod equivalent-constructs ((variant1 VariantC) (variant2 VariantC)) 827 "variant items are (TMDM(5.5)-)equal if the values of their 828 [value], [datatype], [scope], and [parent] properties are equal" 829 (and (string= (charvalue variant1) (charvalue variant2)) 830 (or (and (not (slot-boundp variant1 'datatype)) (not (slot-boundp variant2 'datatype))) 831 (and (slot-boundp variant1 'datatype) (slot-boundp variant2 'datatype) 832 (string= (datatype variant1) (datatype variant2)))) 833 (not (set-exclusive-or (themes variant1) (themes variant2) :key #'internal-id)))) 834 835 836 837 838 ;;;;;;;;;;;;;; 839 ;; 840 ;; NameC 841 842 (elephant:defpclass NameC (CharacteristicC) 843 ((variants ;:accessor variants 844 :associate (VariantC name))) 845 (:documentation "Scoped name of a topic")) 846 847 848 (defgeneric variants (name &key revision) 849 (:method ((name NameC) &key (revision *TM-REVISION*)) 850 (filter-slot-value-by-revision name 'variants :start-revision revision))) 851 852 853 (defgeneric NameC-p (object) 854 (:documentation "test if object is a of type NameC") 855 (:method ((object t)) nil) 856 (:method ((object NameC)) object)) 857 858 859 (defmethod find-all-equivalent ((construct NameC)) 860 (let 861 ((parent (and (slot-boundp construct 'topic) 862 (topic construct)))) 863 (when parent 864 (delete-if-not 865 #'(lambda (cand) (strictly-equivalent-constructs construct cand)) 866 (slot-value parent 'names))))) 867 2339 (dolist (characteristic-assoc-to-delete (slot-p construct 'parent)) 2340 (delete-construct characteristic-assoc-to-delete))) 2341 2342 2343 (defmethod owned-p ((construct CharacteristicC)) 2344 (when (slot-p construct 'parent) 2345 t)) 2346 2347 2348 (defmethod parent ((construct CharacteristicC) &key (revision *TM-REVISION*)) 2349 (let ((valid-associations 2350 (filter-slot-value-by-revision construct 'parent 2351 :start-revision revision))) 2352 (when valid-associations 2353 (parent-construct (first valid-associations))))) 2354 2355 2356 (defmethod add-parent ((construct CharacteristicC) 2357 (parent-construct ReifiableConstructC) 2358 &key (revision *TM-REVISION*)) 2359 (declare (integer revision)) 2360 (let ((already-set-parent (parent construct :revision revision)) 2361 (same-parent-assoc ;should contain an object that was marked as deleted 2362 (loop for parent-assoc in (slot-p construct 'parent) 2363 when (eql parent-construct (parent-construct parent-assoc)) 2364 return parent-assoc))) 2365 (when (and already-set-parent 2366 (not (eql already-set-parent parent-construct))) 2367 (error (make-tm-reference-condition (format nil "From add-parent(): ~a can't be owned by ~a since it is already owned by ~a" 2368 construct parent-construct already-set-parent) 2369 construct (parent construct :revision revision) parent-construct))) 2370 (let ((merged-char 2371 (merge-if-equivalent construct parent-construct :revision revision))) 2372 (if merged-char 2373 merged-char 2374 (progn 2375 (cond (already-set-parent 2376 (let ((parent-assoc 2377 (loop for parent-assoc in (slot-p construct 'parent) 2378 when (eql parent-construct 2379 (parent-construct parent-assoc)) 2380 return parent-assoc))) 2381 (add-to-version-history parent-assoc 2382 :start-revision revision))) 2383 (same-parent-assoc 2384 (add-to-version-history same-parent-assoc 2385 :start-revision revision)) 2386 (t 2387 (let ((association-type (cond ((typep construct 'OccurrenceC) 2388 'OccurrenceAssociationC) 2389 ((typep construct 'NameC) 2390 'NameAssociationC) 2391 (t 2392 'VariantAssociationC)))) 2393 (make-construct association-type 2394 :characteristic construct 2395 :parent-construct parent-construct 2396 :start-revision revision)))) 2397 (when (typep parent-construct 'VersionedConstructC) 2398 (add-to-version-history parent-construct :start-revision revision)) 2399 construct))))) 2400 2401 2402 (defmethod private-delete-parent ((construct CharacteristicC) 2403 (parent-construct ReifiableConstructC) 2404 &key (revision (error (make-missing-argument-condition "From private-delete-parent(): revision must be set" 'revision 'private-delete-parent)))) 2405 (let ((assoc-to-delete 2406 (loop for parent-assoc in (slot-p construct 'parent) 2407 when (eql (parent-construct parent-assoc) parent-construct) 2408 return parent-assoc))) 2409 (when assoc-to-delete 2410 (mark-as-deleted assoc-to-delete :revision revision) 2411 construct))) 2412 2413 2414 (defmethod delete-parent ((construct CharacteristicC) 2415 (parent-construct ReifiableConstructC) 2416 &key (revision (error (make-missing-argument-condition "From delete-parent(): revision must be set" 'revision 'delete-parent)))) 2417 (let ((parent (parent construct :revision revision))) 2418 (when (private-delete-parent construct parent-construct :revision revision) 2419 (when parent 2420 (add-version-info parent revision)) 2421 construct))) 2422 2423 2424 ;;; OccurrenceC 2425 (defmethod equivalent-constructs ((construct-1 OccurrenceC) (construct-2 OccurrenceC) 2426 &key (revision *TM-REVISION*)) 2427 (declare (ignorable revision)) 2428 (and (call-next-method) 2429 (string= (datatype construct-1) (datatype construct-2)))) 2430 2431 2432 (defgeneric OccurrenceC-p (class-symbol) 2433 (:documentation "Returns t if the passed symbol is equal to OccurrenceC.") 2434 (:method ((class-symbol symbol)) 2435 (eql class-symbol 'OccurrenceC))) 2436 2437 2438 (defmethod equivalent-construct ((construct OccurrenceC) 2439 &key (start-revision *TM-REVISION*) 2440 (charvalue "") (themes nil) (instance-of nil) 2441 (datatype "")) 2442 "Occurrences are equal if their charvalue, datatype, themes and 2443 instance-of properties are equal." 2444 (declare (type (or null TopicC) instance-of) (string datatype) 2445 (ignorable start-revision charvalue themes instance-of)) 2446 (let ((equivalent-characteristic (call-next-method))) 2447 ;; item-identifiers and reifers are not checked because the equaity have to 2448 ;; be variafied without them 2449 (and equivalent-characteristic 2450 (string= (datatype construct) datatype)))) 2451 2452 2453 ;;; VariantC 2454 (defmethod find-self-or-equal ((construct VariantC) (parent-construct NameC) 2455 &key (revision *TM-REVISION*)) 2456 (declare (integer revision)) 2457 (let ((vars (variants parent-construct :revision revision))) 2458 (let ((self (find construct vars))) 2459 (if self 2460 self 2461 (let ((equal-var 2462 (remove-if #'null 2463 (map 'list 2464 #'(lambda(var) 2465 (strictly-equivalent-constructs 2466 var construct :revision revision)) 2467 vars)))) 2468 (when equal-var 2469 (first equal-var))))))) 2470 2471 2472 (defmethod equivalent-constructs ((construct-1 VariantC) (construct-2 VariantC) 2473 &key (revision *TM-REVISION*)) 2474 (declare (ignorable revision)) 2475 (and (call-next-method) 2476 (string= (datatype construct-1) (datatype construct-2)))) 2477 2478 2479 (defgeneric VariantC-p (class-symbol) 2480 (:documentation "Returns t if the passed symbol is equal to VariantC.") 2481 (:method ((class-symbol symbol)) 2482 (eql class-symbol 'VariantC))) 2483 2484 2485 (defmethod equivalent-construct ((construct VariantC) 2486 &key (start-revision *TM-REVISION*) 2487 (charvalue "") (themes nil) (datatype "")) 2488 "Variants are equal if their charvalue, datatype and themes 2489 properties are equal." 2490 (declare (string datatype) (ignorable start-revision charvalue themes)) 2491 ;; item-identifiers and reifers are not checked because the equality have to 2492 ;; be variafied without them 2493 (let ((equivalent-characteristic (call-next-method))) 2494 (and equivalent-characteristic 2495 (string= (datatype construct) datatype)))) 2496 2497 2498 ;;; NameC 2499 (defmethod get-all-characteristics ((parent-construct NameC) 2500 (characteristic-symbol symbol)) 2501 (when (VariantC-p characteristic-symbol) 2502 (map 'list #'characteristic (slot-p parent-construct 'variants)))) 2503 2504 2505 (defgeneric NameC-p (class-symbol) 2506 (:documentation "Returns t if the passed symbol is equal to Name.") 2507 (:method ((class-symbol symbol)) 2508 (eql class-symbol 'NameC))) 2509 2510 2511 (defgeneric complete-name (construct variants &key start-revision) 2512 (:documentation "Adds all given variants to the passed construct.") 2513 (:method ((construct NameC) (variants list) 2514 &key (start-revision *TM-REVISION*)) 2515 (dolist (variant variants) 2516 (add-variant construct variant :revision start-revision)) 2517 construct)) 2518 2519 2520 (defmethod equivalent-construct ((construct NameC) 2521 &key (start-revision *TM-REVISION*) 2522 (charvalue "") (themes nil) (instance-of nil)) 2523 "Names are equal if their charvalue, instance-of and themes properties 2524 are equal." 2525 (declare (type (or null TopicC) instance-of) 2526 (ignorable start-revision charvalue instance-of themes)) 2527 (call-next-method)) 2528 868 2529 869 2530 (defmethod delete-construct :before ((construct NameC)) 870 (dolist (variant (variants construct)) 871 (delete-construct variant))) 872 873 874 (defmethod equivalent-constructs ((name1 NameC) (name2 NameC)) 875 "check for the equlity of two names by the TMDM's equality 876 rules (5.4)" 877 (and 878 (string= (charvalue name1) (charvalue name2)) 879 (or (and (instance-of-p name1) 880 (instance-of-p name2) 881 (= (internal-id (instance-of name1)) 882 (internal-id (instance-of name2)))) 883 (and (not (instance-of-p name1)) (not (instance-of-p name2)))) 884 (not (set-exclusive-or (themes name1) (themes name2) :key #'internal-id)))) 885 886 887 888 889 ;;;;;;;;;;;;;; 890 ;; 891 ;; OccurrenceC 892 893 (elephant:defpclass OccurrenceC (CharacteristicC) 894 ((datatype :accessor datatype 895 :initarg :datatype 896 :initform nil 897 :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)"))) 898 899 900 (defgeneric OccurrenceC-p (object) 901 (:documentation "test if object is a of type OccurrenceC") 902 (:method ((object t)) nil) 903 (:method ((object OccurrenceC)) object)) 904 905 (defmethod find-all-equivalent ((construct OccurrenceC)) 906 (let 907 ((parent (and (slot-boundp construct 'topic) 908 (topic construct)))) 909 (when parent 910 (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) 911 (slot-value parent 'occurrences))))) 912 913 (defmethod equivalent-constructs ((occ1 OccurrenceC) (occ2 OccurrenceC)) 914 "Occurrence items are equal if the values of their [value], [datatype], [scope], [type], and [parent] properties are equal (TMDM 5.6)" 915 (and 916 (string= (charvalue occ1) (charvalue occ2)) 917 (not (set-exclusive-or (themes occ1) (themes occ2) :key #'internal-id)) 918 (= (internal-id (topic occ1)) (internal-id (topic occ2))) 919 (or 920 (and (instance-of-p occ1) (instance-of-p occ2) 921 (= 922 (internal-id (instance-of occ1)) 923 (internal-id (instance-of occ2)))) 924 (and (not (instance-of-p occ1)) (not (instance-of-p occ2)))))) 925 926 927 ;;;;;;;;;;;;;;;;; 928 ;; 929 ;; TopicC 930 931 (elephant:defpclass TopicC (ReifiableConstructC) 932 ((topic-identifiers 933 :accessor topic-identifiers 934 :associate (TopicIdentificationC identified-construct)) 935 (psis ;accessor written below 936 :associate (PersistentIdC identified-construct) 937 :documentation "list of PSI objects associated with this 938 topic") 939 (locators 940 ;accessor written below 941 :associate (SubjectLocatorC identified-construct) 942 :documentation "an optional URL that (if given) means that this topic is a subject locator") 943 (names ;accessor written below 944 :associate (NameC topic) 945 :documentation "list of topic names (as TopicC objects)") 946 (occurrences ;accessor occurrences explicitly written below 947 :associate (OccurrenceC topic) 948 :documentation "list of occurrences (as OccurrenceC objects)") 949 (player-in-roles ;accessor player-in-roles written below 950 :associate (RoleC player) 951 :documentation "the list of all role instances where this topic is a player in") 952 (used-as-type ;accessor used-as-type written below 953 :associate (TypableC instance-of) 954 :documentation "list of all constructs that have this topic as their type") 955 (used-as-theme ;accessor used-as-theme written below 956 :associate (ScopableC themes) 957 :many-to-many t 958 :documentation "list of all scopable objects this topic is a theme in") 959 (in-topicmaps 960 :associate (TopicMapC topics) 961 :many-to-many t 962 :documentation "list of all topic maps this topic is part of") 963 (reified 964 :associate ReifiableConstructC 965 :documentation "contains a reified object, represented as 1:1 association")) 966 (:documentation "Topic in a Topic Map")) 967 968 969 (defgeneric reified (topic &key revision) 970 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 971 (when (slot-boundp topic 'reified) 972 (slot-value topic 'reified)))) 973 974 (defgeneric (setf reified) (reifiable ReifiableConstructC) 975 (:method (reifiable (topic TopicC)) 976 (setf (slot-value topic 'reified) reifiable))) 977 ; (setf (reifier reifiable) topic))) 978 979 (defgeneric occurrences (topic &key revision) 980 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 981 (filter-slot-value-by-revision topic 'occurrences :start-revision revision))) 982 983 (defgeneric names (topic &key revision) 984 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 985 (filter-slot-value-by-revision topic 'names :start-revision revision))) 986 987 (defgeneric psis (topic &key revision) 988 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 989 (filter-slot-value-by-revision 990 topic 'psis :start-revision revision))) 991 992 (defgeneric locators (topic &key revision) 993 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 994 (filter-slot-value-by-revision 995 topic 'locators :start-revision revision))) 996 997 (defgeneric player-in-roles (topic &key revision) 998 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 999 (filter-slot-value-by-revision 1000 topic 'player-in-roles :start-revision revision))) 1001 1002 (defgeneric used-as-type (topic &key revision) 1003 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 1004 (filter-slot-value-by-revision topic 'used-as-type :start-revision revision))) 1005 1006 (defgeneric used-as-theme (topic &key revision) 1007 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 1008 (filter-slot-value-by-revision topic 'used-as-theme :start-revision revision))) 1009 1010 (defgeneric in-topicmaps (topic &key revision) 1011 (:method ((topic TopicC) &key (revision *TM-REVISION*)) 1012 (filter-slot-value-by-revision topic 'in-topicmaps :start-revision revision))) 1013 1014 (defun move-identifiers(destination-topic source-topic &key (what 'item-identifiers)) 1015 "Moves all identifiers from the source-topic to the destination topic." 1016 (declare (TopicC destination-topic source-topic)) 1017 (let ((all-source-identifiers 1018 (cond 1019 ((eql what 'item-identifiers) 1020 (item-identifiers source-topic)) 1021 ((eql what 'locators) 1022 (locators source-topic)) 1023 (t 1024 (psis source-topic)))) 1025 (all-destination-identifiers 1026 (cond 1027 ((eql what 'item-identifiers) 1028 (item-identifiers destination-topic)) 1029 ((eql what 'locators) 1030 (locators destination-topic)) 1031 ((eql what 'psis) 1032 (psis destination-topic)) 1033 ((eql what 'topic-identifiers) 1034 (topic-identifiers destination-topic))))) 1035 (let ((identifiers-to-move 1036 (loop for id in all-source-identifiers 1037 when (not (find-if #'(lambda(x) 1038 (if (eql what 'topic-identifiers) 1039 (string= (xtm-id x) (xtm-id id)) 1040 (string= (uri x) (uri id)))) 1041 all-destination-identifiers)) 1042 collect id))) 1043 (dolist (item identifiers-to-move) 1044 (remove-association source-topic what item) 1045 (add-association destination-topic what item))))) 1046 1047 (defmethod initialize-instance :around ((instance TopicC) &key (psis nil) (locators nil) (reified nil)) 1048 "implement the pseudo-initargs :topic-ids, :persistent-ids, and :subject-locators" 1049 (declare (list psis)) 1050 (declare (list locators)) 1051 (call-next-method) 1052 ;item-identifiers are handled in the around-method for ReifiableConstructs, 1053 ;TopicIdentificationCs are handled in make-construct of TopicC 1054 (dolist (persistent-id psis) 1055 (declare (PersistentIdC persistent-id)) 1056 (setf (identified-construct persistent-id) instance)) 1057 (dolist (subject-locator locators) 1058 (declare (SubjectLocatorC subject-locator)) 1059 (setf (identified-construct subject-locator) instance)) 1060 (when reified 1061 (setf (reified instance) reified))) 1062 1063 1064 (defmethod delete-construct :before ((construct TopicC)) 1065 (dolist (dependent (append (topic-identifiers construct) 1066 (psis construct) 1067 (locators construct) 1068 (names construct) 1069 (occurrences construct) 1070 (player-in-roles construct) 1071 (used-as-type construct))) 1072 (delete-construct dependent)) 1073 (dolist (theme (used-as-theme construct)) 1074 (elephant:remove-association construct 'used-as-theme theme)) 1075 (dolist (tm (in-topicmaps construct)) 1076 (elephant:remove-association construct 'in-topicmaps tm)) 1077 (when (reified construct) 1078 (slot-makunbound (reified construct) 'reifier))) 1079 1080 (defun get-all-constructs-by-uri (uri) 1081 (delete 1082 nil 1083 (mapcar 1084 (lambda (identifier) 1085 (and 1086 (slot-boundp identifier 'identified-construct) 1087 (identified-construct identifier))) 1088 (union 1089 (union 1090 (elephant:get-instances-by-value 'ItemIdentifierC 'uri uri) 1091 (elephant:get-instances-by-value 'PersistentIdC 'uri uri)) 1092 (elephant:get-instances-by-value 'SubjectLocatorC 'uri uri))))) 1093 1094 1095 (defun find-existing-topic (item-identifiers locators psis) 1096 (let 1097 ((uris 1098 (mapcar #'uri 1099 (union (union item-identifiers locators) psis))) 1100 (existing-topics nil)) 1101 (dolist (uri uris) 1102 (setf existing-topics 1103 (nunion existing-topics 1104 (get-all-constructs-by-uri uri) 1105 :key #'internal-id))) 1106 (assert (<= (length existing-topics) 1)) 1107 (first existing-topics))) 1108 1109 1110 (defmethod make-construct ((class-symbol (eql 'TopicC)) &rest args 1111 &key start-revision item-identifiers locators psis topicid xtm-id) 1112 (let 1113 ((existing-topic 1114 (find-existing-topic item-identifiers locators psis))) 1115 (if existing-topic 1116 (progn 1117 ;our problem with topics is that we know only after the 1118 ;addition of all the identifiers and characteristics if 1119 ;anything has changed. We can't decide that here, so we must 1120 ;add all revisions (real or imaginary) to version history 1121 ;and decide the rest in changed-p. Maybe somebody can think 1122 ;of a better way? 1123 (add-to-version-history existing-topic 1124 :start-revision start-revision) 1125 (init-topic-identification existing-topic topicid xtm-id 1126 :revision start-revision) 1127 (let* ;add new identifiers to existing topics 1128 ((all-new-identifiers 1129 (union (union item-identifiers locators) psis)) 1130 (all-existing-identifiers 1131 (get-all-identifiers-of-construct existing-topic))) 1132 (mapc 1133 (lambda (identifier) 1134 (setf (identified-construct identifier) existing-topic)) 1135 (set-difference all-new-identifiers all-existing-identifiers 1136 :key #'uri :test #'string=)) 1137 (mapc #'delete-construct 1138 (delete-if 1139 (lambda (identifier) 1140 (slot-boundp identifier 'identified-construct)) 1141 all-new-identifiers))) 1142 (check-for-duplicate-identifiers existing-topic) 1143 existing-topic) 1144 (progn 1145 (let* 1146 ((cleaned-args (remove-nil-values args)) 1147 (new-topic 1148 (apply #'make-instance 'TopicC cleaned-args))) 1149 1150 (init-topic-identification new-topic topicid xtm-id 1151 :revision start-revision) 1152 (check-for-duplicate-identifiers new-topic) 1153 (add-to-version-history new-topic 1154 :start-revision start-revision) 1155 new-topic))))) 1156 1157 (defmethod make-construct :around ((class-symbol (eql 'TopicC)) 1158 &key start-revision &allow-other-keys) 1159 (declare (ignorable start-revision)) 2531 (let ((variant-assocs-to-delete (slot-p construct 'variants))) 2532 (let ((all-variants (map 'list #'characteristic variant-assocs-to-delete))) 2533 (dolist (variant-assoc-to-delete variant-assocs-to-delete) 2534 (delete-construct variant-assoc-to-delete)) 2535 (dolist (candidate-to-delete all-variants) 2536 (unless (owned-p candidate-to-delete) 2537 (delete-construct candidate-to-delete)))))) 2538 2539 2540 (defgeneric variants (construct &key revision) 2541 (:documentation "Returns all variants that correspond with the given revision 2542 and that are associated with the passed construct.") 2543 (:method ((construct NameC) &key (revision *TM-REVISION*)) 2544 (let ((valid-associations 2545 (filter-slot-value-by-revision construct 'variants 2546 :start-revision revision))) 2547 (map 'list #'characteristic valid-associations)))) 2548 2549 2550 (defgeneric add-variant (construct variant &key revision) 2551 (:documentation "Adds the given theme-topic to the passed 2552 scopable-construct.") 2553 (:method ((construct NameC) (variant VariantC) 2554 &key (revision *TM-REVISION*)) 2555 (when (and (parent variant :revision revision) 2556 (not (eql (parent variant :revision revision) construct))) 2557 (error (make-tm-reference-condition (format nil "From add-variant(): ~a can't be owned by ~a since it is already owned by the name ~a" 2558 variant construct (parent variant :revision revision)) 2559 variant (parent variant :revision revision) construct))) 2560 (if (merge-if-equivalent variant construct :revision revision) 2561 construct 2562 (let ((all-variants 2563 (map 'list #'characteristic (slot-p construct 'variants)))) 2564 (if (find variant all-variants) 2565 (let ((variant-assoc 2566 (loop for variant-assoc in (slot-p construct 'variants) 2567 when (eql (characteristic variant-assoc) variant) 2568 return variant-assoc))) 2569 (add-to-version-history variant-assoc :start-revision revision)) 2570 (make-construct 'VariantAssociationC 2571 :characteristic variant 2572 :parent-construct construct 2573 :start-revision revision)) 2574 (when (parent construct :revision revision) 2575 (add-name (parent construct :revision revision) construct 2576 :revision revision)) 2577 construct)))) 2578 2579 2580 (defgeneric private-delete-variant (construct variant &key revision) 2581 (:documentation "Deletes the passed variant by marking it's association as 2582 deleted in the passed revision.") 2583 (:method ((construct NameC) (variant VariantC) 2584 &key (revision (error (make-missing-argument-condition "From private-delete-variant(): revision must be set" 'revision 'private-delete-variant)))) 2585 (let ((assoc-to-delete (loop for variant-assoc in (slot-p construct 2586 'variants) 2587 when (eql (characteristic variant-assoc) variant) 2588 return variant-assoc))) 2589 (when assoc-to-delete 2590 (mark-as-deleted assoc-to-delete :revision revision) 2591 construct)))) 2592 2593 2594 (defgeneric delete-variant (construct variant &key revision) 2595 (:documentation "See private-delete-variant but adds a the parent 2596 and the parent's parent to the given version history.") 2597 (:method ((construct NameC) (variant VariantC) 2598 &key (revision (error (make-missing-argument-condition "From delete-variant(): revision must be set" 'revision 'delete-variant)))) 2599 (when (private-delete-variant construct variant :revision revision) 2600 (when (parent construct :revision revision) 2601 (add-name (parent construct :revision revision) construct 2602 :revision revision) 2603 construct)))) 2604 2605 2606 (defmethod add-characteristic ((construct NameC) (characteristic VariantC) 2607 &key (revision *TM-REVISION*)) 2608 (declare (integer revision)) 2609 (add-variant construct characteristic :revision revision)) 2610 2611 2612 (defmethod private-delete-characteristic ((construct NameC) (characteristic VariantC) 2613 &key (revision (error (make-missing-argument-condition "From private-delete-characteristic(): revision must be set" 'revision 'private-delete-characteristic)))) 2614 (declare (integer revision)) 2615 (private-delete-variant construct characteristic :revision revision)) 2616 2617 2618 (defmethod delete-characteristic ((construct NameC) (characteristic VariantC) 2619 &key (revision (error (make-missing-argument-condition "From delete-characteristic(): revision must be set" 'revision 'delete-characteristic)))) 2620 (declare (integer revision)) 2621 (delete-variant construct characteristic :revision revision)) 2622 2623 2624 ;;; AssociationC 2625 (defmethod mark-as-deleted :around ((ass AssociationC) &key source-locator revision) 2626 "Marks an association and its roles as deleted" 2627 (mapc (lambda (role) 2628 (mark-as-deleted role :revision revision :source-locator source-locator)) 2629 (roles ass :revision 0)) 1160 2630 (call-next-method)) 1161 2631 1162 1163 (defmethod equivalent-constructs ((topic1 TopicC) (topic2 TopicC)) 1164 "TMDM, 5.3.5: Equality rule: Two topic items are equal if they have: 1165 1166 * at least one equal string in their [subject identifiers] properties, 1167 1168 * at least one equal string in their [item identifiers] properties, 1169 1170 * at least one equal string in their [subject locators] properties, 1171 1172 * an equal string in the [subject identifiers] property of the one 1173 topic item and the [item identifiers] property of the other, or the 1174 same information item in their [reified] properties (TODO: this rule 1175 is currently ignored)" 1176 ;(declare (optimize (debug 3))) 1177 (let 1178 ((psi-uris1 1179 (map 'list #'uri (psis topic1))) 1180 (psi-uris2 1181 (map 'list #'uri (psis topic2))) 1182 (ii-uris1 1183 (map 'list #'uri (item-identifiers topic1))) 1184 (ii-uris2 1185 (map 'list #'uri (item-identifiers topic2))) 1186 (locators1 1187 (map 'list #'uri (locators topic1))) 1188 (locators2 1189 (map 'list #'uri (locators topic2)))) 1190 (let 1191 ((all-uris1 1192 (union psi-uris1 (union ii-uris1 locators1) :test #'string=)) 1193 (all-uris2 1194 (union psi-uris2 (union ii-uris2 locators2) :test #'string=))) 1195 ;;TODO: consider what we should do about this. If the topic at a 1196 ;;given revision doesn't exist yet, it correctly has no uris 1197 ;;(for that version) 1198 ;; (when (= 0 (length all-uris1)) 1199 ;; (error (make-condition 'no-identifier-error :message "Topic1 has no identifier" :internal-id (internal-id topic1)))) 1200 ;; (when (= 0 (length all-uris2)) 1201 ;; (error (make-condition 'no-identifier-error :message "Topic2 has no identifier" :internal-id (internal-id topic2)))) 1202 (intersection 1203 all-uris1 all-uris2 1204 :test #'string=)))) 1205 1206 (defmethod get-all-identifiers-of-construct ((top TopicC)) 1207 (append (psis top) 1208 (locators top) 1209 (item-identifiers top))) 1210 1211 1212 (defmethod topicid ((top TopicC) &optional (xtm-id nil)) 1213 "Return the primary id of this item (= essentially the OID). If 1214 xtm-id is explicitly given, return one of the topicids in that 1215 TM (which must then exist)" 1216 (if xtm-id 1217 (let 1218 ((possible-identifications 1219 (remove-if-not 1220 (lambda (top-id) 1221 (string= (xtm-id top-id) xtm-id)) 1222 (elephant:get-instances-by-value 1223 'TopicIdentificationC 1224 'identified-construct 1225 top)))) 1226 (unless possible-identifications 1227 (error (make-condition 1228 'object-not-found-error 1229 :message 1230 (format nil "Could not find an object ~a in xtm-id ~a" top xtm-id)))) 1231 (uri (first possible-identifications))) 1232 (format nil "t~a" 1233 (internal-id top)))) 1234 1235 1236 (defgeneric psis-p (top) 1237 (:documentation "Test for the existence of PSIs") 1238 (:method ((top TopicC)) (slot-predicate top 'psis))) 1239 1240 (defgeneric list-instanceOf (topic &key tm) 1241 (:documentation "Generate a list of all topics that this topic is an 1242 instance of, optionally filtered by a topic map")) 1243 1244 (defmethod list-instanceOf ((topic TopicC) &key (tm nil)) 1245 (remove-if 1246 #'null 1247 (map 'list #'(lambda(x) 1248 (when (loop for psi in (psis (instance-of x)) 1249 when (string= (uri psi) "http://psi.topicmaps.org/iso13250/model/instance") 1250 return t) 1251 (loop for role in (roles (parent x)) 1252 when (not (eq role x)) 1253 return (player role)))) 1254 (if tm 1255 (remove-if-not 1256 (lambda (role) 1257 ;(format t "player: ~a" (player role)) 1258 ;(format t "parent: ~a" (parent role)) 1259 ;(format t "topic: ~a~&" topic) 1260 (in-topicmap tm (parent role))) 1261 (player-in-roles topic)) 1262 (player-in-roles topic))))) 1263 1264 1265 (defgeneric list-super-types (topic &key tm) 1266 (:documentation "Generate a list of all topics that this topic is an 1267 subclass of, optionally filtered by a topic map")) 1268 1269 1270 (defmethod list-super-types ((topic TopicC) &key (tm nil)) 1271 (remove-if 1272 #'null 1273 (map 'list #'(lambda(x) 1274 (when (loop for psi in (psis (instance-of x)) 1275 when (string= (uri psi) *subtype-psi*) 1276 return t) 1277 (loop for role in (roles (parent x)) 1278 when (not (eq role x)) 1279 return (player role)))) 1280 (if tm 1281 (remove-if-not 1282 (lambda (role) 1283 (format t "player: ~a" (player role)) 1284 (format t "parent: ~a" (parent role)) 1285 (format t "topic: ~a~&" topic) 1286 (in-topicmap tm (parent role))) 1287 (player-in-roles topic)) 1288 (player-in-roles topic))))) 1289 1290 1291 (defun string-starts-with (str prefix) 1292 "Checks if string str starts with a given prefix" 1293 (declare (string str prefix)) 1294 (string= str prefix :start1 0 :end1 1295 (min (length prefix) 1296 (length str)))) 1297 1298 1299 (defun get-item-by-item-identifier (uri &key revision) 1300 "get a construct by its item identifier. Returns nil if the item does not exist in a 1301 particular revision" 1302 (declare (string uri)) 1303 (declare (integer revision)) 1304 (let 1305 ((ii-obj 1306 (elephant:get-instance-by-value 'ItemIdentifierC 1307 'uri uri))) 1308 (when ii-obj 1309 (find-item-by-revision 1310 (identified-construct ii-obj) revision)))) 1311 1312 1313 (defun get-item-by-psi (psi &key (revision 0)) 1314 "get a topic by its PSI. Returns nil if the item does not exist in a 1315 particular revision" 1316 (declare (string psi)) 1317 (declare (integer revision)) 1318 (let 1319 ((psi-obj 1320 (elephant:get-instance-by-value 'PersistentIdC 1321 'uri psi))) 1322 (when psi-obj 1323 (find-item-by-revision 1324 (identified-construct psi-obj) revision)))) 1325 1326 (defun get-item-by-id (topicid &key (xtm-id *current-xtm*) (revision 0) (error-if-nil nil)) 1327 "get a topic by its id, assuming a xtm-id. If xtm-id is empty, the current TM 1328 is chosen. If xtm-id is nil, choose the global TM with its internal ID, if 1329 applicable in the correct revision. If revison is provided, then the code checks 1330 if the topic already existed in this revision and returns nil otherwise. 1331 If no item meeting the constraints was found, then the return value is either 1332 NIL or an error is thrown, depending on error-if-nil." 1333 (declare (integer revision)) 1334 (let 1335 ((result 1336 (if xtm-id 1337 (let 1338 ((possible-items 1339 (delete-if-not 1340 (lambda (top-id) 1341 (and 1342 (string= (xtm-id top-id) xtm-id) 1343 (string= (uri top-id) topicid))) ;fixes a bug in 1344 ;get-instances-by-value 1345 ;that does a 1346 ;case-insensitive 1347 ;comparision 1348 (elephant:get-instances-by-value 1349 'TopicIdentificationC 1350 'uri 1351 topicid)))) 1352 (when (and possible-items 1353 (identified-construct-p (first possible-items))) 1354 (unless (= (length possible-items) 1) 1355 (error (make-condition 'duplicate-identifier-error 1356 :message 1357 (format nil "(length possible-items ~a) for id ~a und xtm-id ~a > 1" possible-items topicid xtm-id) 1358 :uri topicid))) 1359 (let 1360 ((found-topic 1361 (identified-construct (first possible-items)))) 1362 (if (= revision 0) 1363 found-topic 1364 (find-item-by-revision found-topic revision))))) 1365 (elephant::controller-recreate-instance elephant:*store-controller* (subseq topicid 1))))) 1366 (if (and error-if-nil (not result)) 1367 (error (format nil "no such item (id: ~a, tm: ~a, rev: ~a)" topicid xtm-id revision)) 1368 result))) 1369 1370 1371 ;;;;;;;;;;;;;;;;;; 1372 ;; 1373 ;; RoleC 1374 1375 (elephant:defpclass RoleC (ReifiableConstructC TypableC) 1376 ((parent :accessor parent 1377 :initarg :parent 1378 :associate AssociationC 1379 :documentation "Association that this role belongs to") 1380 (player :accessor player 1381 :initarg :player 1382 :associate TopicC 1383 :documentation "references the topic that is the player in this role")) 1384 (:documentation "The role that this topic plays in an association (formerly member)")) 1385 1386 1387 1388 (defgeneric RoleC-p (object) 1389 (:documentation "test if object is a of type RoleC") 1390 (:method ((object t)) nil) 1391 (:method ((object RoleC)) object)) 1392 1393 1394 (defgeneric parent-p (vi) 1395 (:documentation "t if this construct has a parent construct") 1396 (:method ((constr RoleC)) (slot-predicate constr 'parent))) 1397 1398 1399 (defmethod delete-construct :before ((construct RoleC)) 1400 ;the way we use roles, we cannot just delete the parent association 1401 ;(at least the second role won't have one left then and will 1402 ;complain) 1403 (delete-1-n-association construct 'parent) 1404 (delete-1-n-association construct 'player)) 1405 1406 (defmethod find-all-equivalent ((construct RoleC)) 1407 (let 1408 ((parent (and (slot-boundp construct 'parent) 1409 (parent construct)))) 1410 (when parent 1411 (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) 1412 (slot-value parent 'roles))))) 1413 1414 1415 (defmethod equivalent-constructs ((role1 RoleC) (role2 RoleC)) 1416 "Association role items are equal if the values of their [type], [player], and [parent] properties are equal (TMDM 5.8)" 1417 ;for the purposes for which we use this method (namely the 1418 ;construction of associations), roles will initially always be 1419 ;unequal regarding their parent properties 1420 (and 1421 (= (internal-id (instance-of role1)) (internal-id (instance-of role2))) 1422 (= (internal-id (player role1)) (internal-id (player role2))))) 1423 1424 1425 ;;;;;;;;;;;;;;;;;; 1426 ;; 1427 ;; AssociationC 1428 1429 (elephant:defpclass AssociationC (ReifiableConstructC ScopableC TypableC) 1430 ((roles :accessor roles 1431 :associate (RoleC parent) 1432 :documentation "(non-empty) list of this association's roles") 1433 (in-topicmaps 1434 :associate (TopicMapC associations) 1435 :many-to-many t 1436 :documentation "list of all topic maps this association is part of")) 1437 (:documentation "Association in a Topic Map") 1438 (:index t)) 2632 2633 (defmethod equivalent-constructs ((construct-1 AssociationC) 2634 (construct-2 AssociationC) 2635 &key (revision *TM-REVISION*)) 2636 (declare (ignorable revision)) 2637 (and (eql (instance-of construct-1 :revision revision) 2638 (instance-of construct-2 :revision revision)) 2639 (not (set-exclusive-or (themes construct-1 :revision revision) 2640 (themes construct-2 :revision revision))) 2641 2642 (not (set-exclusive-or 2643 (roles construct-1 :revision revision) 2644 (roles construct-2 :revision revision) 2645 :test #'(lambda(role-1 role-2) 2646 (strictly-equivalent-constructs role-1 role-2 2647 :revision revision)))))) 2648 2649 2650 (defgeneric AssociationC-p (class-symbol) 2651 (:documentation "Returns t if the passed symbol is equal to AssociationC.") 2652 (:method ((class-symbol symbol)) 2653 (eql class-symbol 'AssociationC))) 2654 2655 2656 (defmethod equivalent-construct ((construct AssociationC) 2657 &key (start-revision *TM-REVISION*) 2658 (roles nil) (instance-of nil) (themes nil)) 2659 "Associations are equal if their themes, instance-of and roles 2660 properties are equal. 2661 To avoid ceation of duplicate roles the parameter roles is a list of plists 2662 of the form: ((:player <TopicC> :instance-of <TopicC> 2663 :item-identifiers <(ItemIdentifierC)> :reifier <TopicC>))." 2664 (declare (integer start-revision) (list roles themes) 2665 (type (or null TopicC) instance-of)) 2666 ;; item-identifiers and reifers are not checked because the equality have to 2667 ;; be variafied without them 2668 (let ((checked-roles nil)) 2669 (loop for plist in roles 2670 do (let ((found-role 2671 (find-if #'(lambda(assoc-role) 2672 (equivalent-construct 2673 assoc-role :player (getf plist :player) 2674 :start-revision (or (getf plist :start-revision) 2675 start-revision) 2676 :instance-of (getf plist :instance-of))) 2677 (roles construct :revision start-revision)))) 2678 (when found-role 2679 (push found-role checked-roles)))) 2680 (and 2681 (not (set-exclusive-or (roles construct :revision start-revision) 2682 checked-roles)) 2683 (= (length checked-roles) (length roles)) 2684 (equivalent-typable-construct construct instance-of 2685 :start-revision start-revision) 2686 (equivalent-scopable-construct construct themes 2687 :start-revision start-revision)))) 2688 2689 2690 (defmethod delete-construct :before ((construct AssociationC)) 2691 (let ((roles-assocs-to-delete (slot-p construct 'roles))) 2692 (let ((all-roles (map 'list #'role roles-assocs-to-delete))) 2693 (dolist (role-assoc-to-delete roles-assocs-to-delete) 2694 (delete-construct role-assoc-to-delete)) 2695 (dolist (candidate-to-delete all-roles) 2696 (unless (owned-p candidate-to-delete) 2697 (delete-construct candidate-to-delete))) 2698 (dolist (tm (slot-p construct 'in-topicmaps)) 2699 (remove-association construct 'in-topicmaps tm))))) 2700 2701 2702 (defmethod owned-p ((construct AssociationC)) 2703 (when (slot-p construct 'in-topicmaps) 2704 t)) 2705 2706 2707 (defgeneric roles (construct &key revision) 2708 (:documentation "Returns all topics that correspond with the given revision 2709 as a scope for the given topic.") 2710 (:method ((construct AssociationC) &key (revision *TM-REVISION*)) 2711 (let ((valid-associations 2712 (filter-slot-value-by-revision construct 'roles 2713 :start-revision revision))) 2714 (map 'list #'role valid-associations)))) 2715 2716 2717 (defgeneric add-role (construct role &key revision) 2718 (:documentation "Adds the given role to the passed association-construct.") 2719 (:method ((construct AssociationC) (role RoleC) 2720 &key (revision *TM-REVISION*)) 2721 (if (merge-if-equivalent role construct :revision revision) 2722 construct 2723 (let ((all-roles 2724 (map 'list #'role (slot-p construct 'roles)))) 2725 (if (find role all-roles) 2726 (let ((role-assoc 2727 (loop for role-assoc in (slot-p construct 'roles) 2728 when (eql (role role-assoc) role) 2729 return role-assoc))) 2730 (add-to-version-history role-assoc :start-revision revision)) 2731 (make-construct 'RoleAssociationC 2732 :role role 2733 :parent-construct construct 2734 :start-revision revision)) 2735 (add-to-version-history construct :start-revision revision) 2736 construct)))) 2737 2738 2739 (defgeneric private-delete-role (construct role &key revision) 2740 (:documentation "Deletes the passed role by marking it's association as 2741 deleted in the passed revision.") 2742 (:method ((construct AssociationC) (role RoleC) 2743 &key (revision (error (make-missing-argument-condition "From private-delete-role(): revision must be set" 'revision 'private-delete-role)))) 2744 (let ((assoc-to-delete (loop for role-assoc in (slot-p construct 'roles) 2745 when (eql (role role-assoc) role) 2746 return role-assoc))) 2747 (when assoc-to-delete 2748 (mark-as-deleted assoc-to-delete :revision revision) 2749 construct)))) 2750 2751 2752 (defgeneric delete-role (construct role &key revision) 2753 (:documentation "See private-delete-role but adds the parent association 2754 to the given version.") 2755 (:method ((construct AssociationC) (role RoleC) 2756 &key (revision (error (make-missing-argument-condition "From delete-role(): revision must be set" 'revision 'delete-role)))) 2757 (when (private-delete-role construct role :revision revision) 2758 (add-to-version-history construct :start-revision revision) 2759 construct))) 1439 2760 1440 2761 … … 1443 2764 1444 2765 1445 (defgeneric AssociationC-p (object) 1446 (:documentation "test if object is a of type AssociationC") 1447 (:method ((object t)) nil) 1448 (:method ((object AssociationC)) object)) 1449 1450 1451 (defmethod initialize-instance :around ((instance AssociationC) 1452 &key 1453 (roles nil)) 1454 "implements the pseudo-initarg :roles" 1455 (declare (list roles)) 1456 (let 1457 ((association (call-next-method))) 1458 (dolist (role-data roles) 1459 (make-instance 1460 'RoleC 1461 :instance-of (getf role-data :instance-of) 1462 :player (getf role-data :player) 1463 :item-identifiers (getf role-data :item-identifiers) 1464 :reifier (getf role-data :reifier) 1465 :parent association)))) 1466 1467 (defmethod make-construct :around ((class-symbol (eql 'AssociationC)) 1468 &key 1469 start-revision 1470 &allow-other-keys) 1471 (declare (ignorable start-revision)) 1472 (let 1473 ((association 1474 (call-next-method))) 1475 (declare (AssociationC association)) 1476 (dolist (role (slot-value association 'roles)) 1477 (unless (versions role) 1478 (add-to-version-history role 1479 :start-revision start-revision))) 1480 association)) 1481 1482 (defmethod copy-item-identifiers :around 1483 ((from-construct AssociationC) 1484 (to-construct AssociationC)) 1485 "Internal method to copy over item idenfiers from one association 1486 with its roles to another one. Role identifiers are also 1487 copied. Returns nil if neither association nor role identifiers had to be copied" 1488 (let 1489 ((item-identifiers-copied-p nil)) ;rather brutal solution. find a better one 1490 (when (call-next-method) 1491 (setf item-identifiers-copied-p t)) 1492 (do ((from-roles (roles from-construct) (rest from-roles)) 1493 (to-roles (roles to-construct) (rest to-roles))) 1494 ((null from-roles) 'finished) 1495 (let 1496 ((from-role (first from-roles)) 1497 (to-role (first to-roles))) 1498 (when 1499 (mapc 1500 (lambda (identifier) 1501 (setf (identified-construct identifier) 1502 to-role)) 1503 (set-difference (item-identifiers from-role) 1504 (item-identifiers to-role) 1505 :key #'uri :test #'string=)) 1506 (setf item-identifiers-copied-p t)))) 1507 item-identifiers-copied-p)) 1508 1509 (defmethod delete-construct :before ((construct AssociationC)) 1510 (dolist (role (roles construct)) 1511 (delete-construct role)) 1512 (dolist (tm (in-topicmaps construct)) 1513 (elephant:remove-association construct 'in-topicmaps tm))) 1514 1515 (defmethod find-all-equivalent ((construct AssociationC)) 1516 (let 1517 ((some-player (player (or 1518 (second (roles construct)) 1519 (first (roles construct)))))) ;; dirty, dirty... but brings a tenfold speedup! 1520 (delete-if-not 1521 #'(lambda (cand) 1522 (unless (eq construct cand) 1523 (equivalent-constructs construct cand))) 1524 ;here we need to use the "internal" API and access the players 1525 ;with slot-value (otherwise we won't be able to merge with 1526 ;'deleted' associations) 1527 (mapcar #'parent (slot-value some-player 'player-in-roles))))) 1528 1529 1530 (defmethod equivalent-constructs ((assoc1 AssociationC) (assoc2 AssociationC)) 1531 "Association items are equal if the values of their [scope], [type], and [roles] properties are equal (TMDM 5.7)" 1532 (and 1533 (= (internal-id (instance-of assoc1)) (internal-id (instance-of assoc2))) 1534 (not (set-exclusive-or (themes assoc1) (themes assoc2) 1535 :key #'internal-id)) 1536 (not (set-exclusive-or 1537 (roles assoc1) 1538 (roles assoc2) 1539 :test #'equivalent-constructs)))) 1540 1541 1542 (elephant:defpclass TopicMapC (ReifiableConstructC) 1543 ((topics :accessor topics 1544 :associate (TopicC in-topicmaps) 1545 :documentation "list of topics that explicitly belong to this TM") 1546 (associations :accessor associations 1547 :associate (AssociationC in-topicmaps) 1548 :documentation "list of associations that belong to this TM")) 1549 (:documentation "Topic Map")) 1550 1551 (defmethod equivalent-constructs ((tm1 TopicMapC) (tm2 TopicMapC)) 1552 "Topic Map items are equal if one of their identifiers is equal" 1553 ;Note: TMDM does not make any statement to this effect, but it's the 1554 ;one logical assumption 1555 (intersection 1556 (item-identifiers tm1) 1557 (item-identifiers tm2) 1558 :test #'equivalent-constructs)) 1559 1560 (defmethod find-all-equivalent ((construct TopicMapC)) 1561 (let 1562 ((tms (elephant:get-instances-by-class 'd:TopicMapC))) 1563 (delete-if-not 1564 (lambda(tm) 1565 (strictly-equivalent-constructs construct tm)) 1566 tms))) 1567 1568 (defgeneric add-to-topicmap (tm top) 1569 (:documentation "add a topic or an association to a topic 1570 map. Return the added construct")) 1571 1572 (defmethod add-to-topicmap ((tm TopicMapC) (top TopicC)) 1573 ;TODO: add logic not to add pure topic stubs unless they don't exist yet in the store 1574 ; (elephant:add-association tm 'topics top) ;by adding the elephant association in this order, there will be missing one site of this association 1575 (elephant:add-association top 'in-topicmaps tm) 1576 top) 1577 1578 (defmethod add-to-topicmap ((tm TopicMapC) (ass AssociationC)) 1579 ;(elephant:add-association tm 'associations ass) 1580 (elephant:add-association ass 'in-topicmaps tm) 1581 ass) 1582 1583 (defgeneric in-topicmap (tm constr &key revision) 1584 (:documentation "Is a given construct (topic or assiciation) in this topic map?")) 1585 1586 (defmethod in-topicmap ((tm TopicMapC) (top TopicC) &key (revision 0)) 2766 ;;; RoleC 2767 (defmethod mark-as-deleted ((construct RoleC) &key source-locator revision) 2768 "Marks the last active relation between a role and its parent association 2769 as deleted." 2770 (declare (ignorable source-locator)) 2771 (let ((owner (parent construct :revision 0))) 2772 (when owner 2773 (private-delete-role owner construct :revision revision)))) 2774 2775 2776 (defmethod marked-as-deleted-p ((construct RoleC)) 2777 (unless (parent construct :revision 0) 2778 t)) 2779 2780 2781 (defmethod find-self-or-equal ((construct RoleC) (parent-construct AssociationC) 2782 &key (revision *TM-REVISION*)) 2783 (declare (integer revision)) 2784 (let ((p-roles (roles parent-construct :revision revision))) 2785 (let ((self (find construct p-roles))) 2786 (if self 2787 self 2788 (let ((equal-role 2789 (remove-if #'null 2790 (map 'list 2791 #'(lambda(role) 2792 (strictly-equivalent-constructs 2793 role construct :revision revision)) 2794 p-roles)))) 2795 (when equal-role 2796 (first equal-role))))))) 2797 2798 2799 (defmethod delete-if-not-referenced ((construct RoleC)) 2800 (let ((references (slot-p construct 'parent))) 2801 (when (or (not references) 2802 (and (= (length references) 1) 2803 (marked-as-deleted-p (first references)))) 2804 (delete-construct construct)))) 2805 2806 2807 (defmethod find-oldest-construct ((construct-1 RoleC) (construct-2 RoleC)) 2808 (let ((vi-1 (find-version-info (slot-p construct-1 'parent))) 2809 (vi-2 (find-version-info (slot-p construct-2 'parent)))) 2810 (cond ((not (or vi-1 vi-2)) 2811 construct-1) 2812 ((not vi-1) 2813 construct-2) 2814 ((not vi-2) 2815 construct-1) 2816 ((<= (start-revision vi-1) (start-revision vi-2)) 2817 construct-1) 2818 (t 2819 construct-2)))) 2820 2821 2822 (defmethod equivalent-constructs ((construct-1 RoleC) (construct-2 RoleC) 2823 &key (revision *TM-REVISION*)) 2824 (declare (integer revision)) 2825 (and (eql (instance-of construct-1 :revision revision) 2826 (instance-of construct-2 :revision revision)) 2827 (eql (player construct-1 :revision revision) 2828 (player construct-2 :revision revision)))) 2829 2830 2831 (defgeneric RoleC-p (class-symbol) 2832 (:documentation "Returns t if the passed symbol is equal to RoleC.") 2833 (:method ((class-symbol symbol)) 2834 (eql class-symbol 'RoleC))) 2835 2836 2837 (defmethod equivalent-construct ((construct RoleC) 2838 &key (start-revision *TM-REVISION*) 2839 (player nil) (instance-of nil)) 2840 "Roles are equal if their instance-of and player properties are equal." 2841 (declare (integer start-revision) (type (or null TopicC) player instance-of)) 2842 ;; item-identifiers and reifers are not checked because the equality have to 2843 ;; be variafied without them 2844 (and (equivalent-typable-construct construct instance-of 2845 :start-revision start-revision) 2846 (eql player (player construct :revision start-revision)))) 2847 2848 2849 (defmethod find-item-by-revision ((construct RoleC) 2850 (revision integer) &optional parent-construct) 2851 (if parent-construct 2852 (let ((parent-assoc 2853 (let ((assocs 2854 (remove-if 2855 #'null 2856 (map 'list #'(lambda(assoc) 2857 (when (eql (parent-construct assoc) 2858 parent-construct) 2859 assoc)) 2860 (slot-p construct 'parent))))) 2861 (when assocs 2862 (first assocs))))) 2863 (when parent-assoc 2864 (cond ((= revision 0) 2865 (when 2866 (find-most-recent-revision parent-assoc) 2867 construct)) 2868 (t 2869 (when (find-if 2870 #'(lambda(vi) 2871 (and (>= revision (start-revision vi)) 2872 (or (< revision (end-revision vi)) 2873 (= 0 (end-revision vi))))) 2874 (versions parent-assoc)) 2875 construct))))) 2876 nil)) 2877 2878 2879 (defmethod delete-construct :before ((construct RoleC)) 2880 (dolist (role-assoc-to-delete (slot-p construct 'parent)) 2881 (delete-construct role-assoc-to-delete)) 2882 (dolist (player-assoc-to-delete (slot-p construct 'player)) 2883 (delete-construct player-assoc-to-delete))) 2884 2885 2886 (defgeneric player-p (construct) 2887 (:documentation "Returns t if a player is set in this role. 2888 t is also returned if the player is markes-as-deleted.") 2889 (:method ((construct RoleC)) 2890 (when (slot-p construct 'player) 2891 t))) 2892 2893 2894 (defmethod owned-p ((construct RoleC)) 2895 (when (slot-p construct 'parent) 2896 t)) 2897 2898 2899 (defmethod parent ((construct RoleC) &key (revision *TM-REVISION*)) 2900 "Returns the construct's parent corresponding to the given revision." 2901 (let ((valid-associations 2902 (filter-slot-value-by-revision construct 'parent 2903 :start-revision revision))) 2904 (when valid-associations 2905 (parent-construct (first valid-associations))))) 2906 2907 2908 (defmethod add-parent ((construct RoleC) (parent-construct AssociationC) 2909 &key (revision *TM-REVISION*)) 2910 (declare (integer revision)) 2911 (let ((already-set-parent (parent construct :revision revision)) 2912 (same-parent-assoc (loop for parent-assoc in (slot-p construct 'parent) 2913 when (eql parent-construct (parent-construct parent-assoc)) 2914 return parent-assoc))) 2915 (when (and already-set-parent 2916 (not (eql already-set-parent parent-construct))) 2917 (error (make-tm-reference-condition (format nil "From add-parent(): ~a can't be owned by ~a since it is already owned by ~a" 2918 construct parent-construct already-set-parent) 2919 construct (parent construct :revision revision) parent-construct))) 2920 (let ((merged-role 2921 (merge-if-equivalent construct parent-construct :revision revision))) 2922 (if merged-role 2923 merged-role 2924 (progn 2925 (cond (already-set-parent 2926 (let ((parent-assoc 2927 (loop for parent-assoc in (slot-p construct 'parent) 2928 when (eql parent-construct 2929 (parent-construct parent-assoc)) 2930 return parent-assoc))) 2931 (add-to-version-history parent-assoc 2932 :start-revision revision))) 2933 (same-parent-assoc 2934 (add-to-version-history same-parent-assoc 2935 :start-revision revision)) 2936 (t 2937 (make-construct 'RoleAssociationC 2938 :role construct 2939 :parent-construct parent-construct 2940 :start-revision revision))) 2941 (add-to-version-history parent-construct :start-revision revision) 2942 construct))))) 2943 2944 2945 (defmethod private-delete-parent ((construct RoleC) (parent-construct AssociationC) 2946 &key (revision (error (make-missing-argument-condition "From private-delete-parent(): revision must be set" 'revision 'private-delete-parent)))) 2947 (let ((assoc-to-delete 2948 (loop for parent-assoc in (slot-p construct 'parent) 2949 when (eql (parent-construct parent-assoc) parent-construct) 2950 return parent-assoc))) 2951 (when assoc-to-delete 2952 (mark-as-deleted assoc-to-delete :revision revision) 2953 construct))) 2954 2955 2956 (defmethod delete-parent ((construct RoleC) (parent-construct AssociationC) 2957 &key (revision (error (make-missing-argument-condition "From delete-parent(): revision must be set" 'revision 'delete-parent)))) 2958 (when (private-delete-parent construct parent-construct :revision revision) 2959 (add-to-version-history parent-construct :start-revision revision) 2960 construct)) 2961 2962 2963 (defgeneric player (construct &key revision) 2964 (:documentation "Returns the construct's player corresponding to 2965 the given revision.") 2966 (:method ((construct RoleC) &key (revision *TM-REVISION*)) 2967 (let ((valid-associations 2968 (filter-slot-value-by-revision construct 'player 2969 :start-revision revision))) 2970 (when valid-associations 2971 (player-topic (first valid-associations)))))) 2972 2973 2974 (defgeneric add-player (construct player-topic &key revision) 2975 (:documentation "Adds a topic as a player to a role in the given revision.") 2976 (:method ((construct RoleC) (player-topic TopicC) 2977 &key (revision *TM-REVISION*)) 2978 (let ((already-set-player (player construct :revision revision)) 2979 (same-player-assoc 2980 (loop for player-assoc in (slot-p construct 'player) 2981 when (eql (player-topic player-assoc) player-topic) 2982 return player-assoc))) 2983 (when (and already-set-player 2984 (not (eql already-set-player player-topic))) 2985 (error (make-tm-reference-condition (format nil "From add-player(): ~a can't be played by ~a since it is played by ~a" construct player-topic already-set-player) 2986 construct (player construct :revision revision) player-topic))) 2987 (cond (already-set-player 2988 (let ((player-assoc 2989 (loop for player-assoc in (slot-p construct 'player) 2990 when (eql player-topic (player-topic player-assoc)) 2991 return player-assoc))) 2992 (add-to-version-history player-assoc :start-revision revision))) 2993 (same-player-assoc 2994 (add-to-version-history same-player-assoc :start-revision revision)) 2995 (t 2996 (make-construct 'PlayerAssociationC 2997 :parent-construct construct 2998 :player-topic player-topic 2999 :start-revision revision)))) 3000 construct)) 3001 3002 3003 (defgeneric private-delete-player (construct player-topic &key revision) 3004 (:documentation "Deletes the passed topic as a player of the passed role 3005 object by marking its association-object as deleted.") 3006 (:method ((construct RoleC) (player-topic TopicC) 3007 &key (revision (error (make-missing-argument-condition "From private-delete-player(): revision must be set" 'revision 'private-delete-player)))) 3008 (let ((assoc-to-delete 3009 (loop for player-assoc in (slot-p construct 'player) 3010 when (eql (parent-construct player-assoc) construct) 3011 return player-assoc))) 3012 (when assoc-to-delete 3013 (mark-as-deleted assoc-to-delete :revision revision) 3014 construct)))) 3015 3016 3017 (defgeneric delete-player (construct player-topic &key revision) 3018 (:documentation "See delete-player but adds the parent role to 3019 the given version.") 3020 (:method ((construct RoleC) (player-topic TopicC) 3021 &key (revision (error (make-missing-argument-condition "From delete-player(): revision must be set" 'revision 'delete-player)))) 3022 (when (private-delete-player construct player-topic :revision revision) 3023 (let ((assoc (parent construct :revision revision))) 3024 (when assoc 3025 (add-role assoc construct :revision revision) 3026 construct))))) 3027 3028 3029 ;;; ReifiableConstructC 3030 (defmethod mark-as-deleted :around ((construct ReifiableConstructC) 3031 &key source-locator revision) 3032 "Marks all item-identifiers of a given reifiable-construct as deleted." 3033 (declare (ignorable source-locator)) 3034 (call-next-method) 3035 (dolist (ii (item-identifiers construct :revision 0)) 3036 (private-delete-item-identifier construct ii :revision revision))) 3037 3038 3039 (defmethod check-for-duplicate-identifiers ((construct ReifiableConstructC) 3040 &key (revision *TM-REVISION*)) 3041 (declare (integer revision)) 3042 (dolist (id (get-all-identifiers-of-construct construct :revision revision)) 3043 (when (> 3044 (length 3045 (delete-if-not #'(lambda(identifier) 3046 (or (typep identifier 'PersistentIdC) 3047 (typep identifier 'SubjectLocatorC) 3048 (typep identifier 'ItemIdentifierC))) 3049 (union 3050 (elephant:get-instances-by-value 3051 'ItemIdentifierC 'uri (uri id)) 3052 (union 3053 (elephant:get-instances-by-value 3054 'PersistentIdC 'uri (uri id)) 3055 (elephant:get-instances-by-value 3056 'SubjectLocatorC 'uri (uri id)))))) 3057 1) 3058 (error (make-duplicate-identifier-condition (format nil "Duplicate Identifier ~a has been found" (uri id)) (uri id)))))) 3059 3060 3061 (defgeneric ReifiableConstructC-p (class-symbol) 3062 (:documentation "Returns t if the passed symbol is equal to ReifiableConstructC 3063 or one of its subtypes.") 3064 (:method ((class-symbol symbol)) 3065 (or (eql class-symbol 'ReifiableconstructC) 3066 (TopicMapC-p class-symbol) 3067 (TopicC-p class-symbol) 3068 (AssociationC-p class-symbol) 3069 (RoleC-p class-symbol) 3070 (CharacteristicC-p class-symbol)))) 3071 3072 3073 (defgeneric complete-reifiable (construct item-identifiers reifier 3074 &key start-revision) 3075 (:documentation "Adds all item-identifiers and the reifier to the passed 3076 construct.") 3077 (:method ((construct ReifiableConstructC) item-identifiers reifier 3078 &key (start-revision *TM-REVISION*)) 3079 (declare (integer start-revision) (list item-identifiers) 3080 (type (or null TopicC) reifier)) 3081 (let ((merged-construct construct)) 3082 (dolist (ii item-identifiers) 3083 (setf merged-construct 3084 (add-item-identifier merged-construct ii 3085 :revision start-revision))) 3086 (when reifier 3087 (setf merged-construct (add-reifier merged-construct reifier 3088 :revision start-revision))) 3089 merged-construct))) 3090 3091 3092 (defgeneric equivalent-reifiable-construct (construct reifier item-identifiers 3093 &key start-revision) 3094 (:documentation "Returns t if the passed constructs are TMDM equal, i.e 3095 the reifiable construct have to share an item identifier 3096 or reifier.") 3097 (:method ((construct ReifiableConstructC) reifier item-identifiers 3098 &key (start-revision *TM-REVISION*)) 3099 (declare (integer start-revision) (list item-identifiers) 3100 (type (or null TopicC) reifier)) 3101 (or (and (reifier construct :revision start-revision) 3102 (eql reifier (reifier construct :revision start-revision))) 3103 (and (item-identifiers construct :revision start-revision) 3104 (intersection (item-identifiers construct :revision start-revision) 3105 item-identifiers))))) 3106 3107 3108 (defmethod delete-construct :before ((construct ReifiableConstructC)) 3109 (let ((ii-assocs-to-delete (slot-p construct 'item-identifiers)) 3110 (reifier-assocs-to-delete (slot-p construct 'reifier))) 3111 (let ((all-iis (map 'list #'identifier ii-assocs-to-delete))) 3112 (dolist (construct-to-delete (append ii-assocs-to-delete 3113 reifier-assocs-to-delete)) 3114 (delete-construct construct-to-delete)) 3115 (dolist (ii all-iis) 3116 (unless (owned-p ii) 3117 (delete-construct ii)))))) 3118 3119 3120 (defgeneric item-identifiers (construct &key revision) 3121 (:documentation "Returns the ItemIdentifierC-objects that correspond 3122 with the passed construct and the passed version.") 3123 (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) 3124 (let ((assocs (filter-slot-value-by-revision 3125 construct 'item-identifiers :start-revision revision))) 3126 (map 'list #'identifier assocs)))) 3127 3128 3129 (defgeneric reifier (construct &key revision) 3130 (:documentation "Returns the reifier-topic that corresponds 3131 with the passed construct and the passed version.") 3132 (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) 3133 (let ((assocs (filter-slot-value-by-revision 3134 construct 'reifier :start-revision revision))) 3135 (when assocs ;assocs must be nil or a list with exactly one item 3136 (reifier-topic (first assocs)))))) 3137 3138 3139 (defgeneric add-item-identifier (construct item-identifier &key revision) 3140 (:documentation "Adds the passed item-identifier to the passed construct. 3141 If the item-identifier is already related with the passed 3142 construct a new revision is added. 3143 If the passed identifer already identifies another object 3144 the identified-constructs are merged.") 3145 (:method ((construct ReifiableConstructC) (item-identifier ItemIdentifierC) 3146 &key (revision *TM-REVISION*)) 3147 (let ((all-ids 3148 (map 'list #'identifier (slot-p construct 'item-identifiers))) 3149 (construct-to-be-merged 3150 (let ((id-owner (identified-construct item-identifier 3151 :revision revision))) 3152 (when (not (eql id-owner construct)) 3153 id-owner)))) 3154 (when (and construct-to-be-merged 3155 (not (eql (type-of construct-to-be-merged) 3156 (type-of construct)))) 3157 (error (make-not-mergable-condition (format nil "From add-item-identifier(): ~a and ~a can't be merged since the identified-constructs are not of the same type" 3158 construct construct-to-be-merged) 3159 construct construct-to-be-merged))) 3160 (let ((merged-construct construct)) 3161 (cond (construct-to-be-merged 3162 (setf merged-construct 3163 (merge-constructs construct construct-to-be-merged 3164 :revision revision))) 3165 ((find item-identifier all-ids) 3166 (let ((ii-assoc 3167 (loop for ii-assoc in (slot-p construct 'item-identifiers) 3168 when (eql (identifier ii-assoc) item-identifier) 3169 return ii-assoc))) 3170 (add-to-version-history ii-assoc :start-revision revision))) 3171 (t 3172 (make-construct 'ItemIdAssociationC 3173 :parent-construct construct 3174 :identifier item-identifier 3175 :start-revision revision))) 3176 (add-version-info construct revision) 3177 merged-construct)))) 3178 3179 3180 (defgeneric private-delete-item-identifier (construct item-identifier 3181 &key revision) 3182 (:documentation "Sets the association object between the passed constructs 3183 as mark-as-deleted.") 3184 (:method ((construct ReifiableConstructC) (item-identifier ItemIdentifierC) 3185 &key (revision (error (make-missing-argument-condition "From private-delete-item-identifier(): revision must be set" 'revision 'private-delete-item-identifier)))) 3186 (let ((assoc-to-delete (loop for ii-assoc in (slot-p construct 'item-identifiers) 3187 when (eql (identifier ii-assoc) item-identifier) 3188 return ii-assoc))) 3189 (when assoc-to-delete 3190 (mark-as-deleted assoc-to-delete :revision revision) 3191 construct)))) 3192 3193 3194 (defgeneric delete-item-identifier (construct item-identifier 3195 &key revision) 3196 (:documentation "See private-delete-item-identifier but adds the parent 3197 construct to the given version.") 3198 (:method ((construct ReifiableConstructC) (item-identifier ItemIdentifierC) 3199 &key (revision (error (make-missing-argument-condition "From delete-item-identifier(): revision must be set" 'revision 'delete-item-identifier)))) 3200 (when (private-delete-item-identifier construct item-identifier 3201 :revision revision) 3202 (add-version-info construct revision) 3203 construct))) 3204 3205 3206 (defgeneric add-reifier (construct reifier-topic &key revision) 3207 (:documentation "Adds the passed reifier-topic as reifier of the construct. 3208 If the construct is already reified by the given topic 3209 there only is added a new version-info. 3210 If the reifier-topic reifies already another construct 3211 the reified-constructs are merged.") 3212 (:method ((construct ReifiableConstructC) (reifier-topic TopicC) 3213 &key (revision *TM-REVISION*)) 3214 (when (and (reified-construct reifier-topic :revision revision) 3215 (not (equivalent-constructs construct 3216 (reified-construct 3217 reifier-topic :revision revision)))) 3218 (error (make-not-mergable-condition (format nil "From add-reifier(): ~a and ~a can't be merged since the reified-constructs (~a ~a) are not mergable" 3219 reifier-topic (reifier construct :revision revision) (reified-construct reifier-topic :revision revision) construct) 3220 construct (reified-construct reifier-topic :revision revision)))) 3221 (let ((merged-reifier-topic 3222 (if (reifier construct :revision revision) 3223 (merge-constructs (reifier construct :revision revision) 3224 reifier-topic) 3225 reifier-topic))) 3226 (let ((all-constructs (map 'list #'reifiable-construct 3227 (slot-p reifier-topic 'reified-construct)))) 3228 (let ((merged-construct construct)) 3229 (cond ((reified-construct merged-reifier-topic :revision revision) 3230 (let ((merged-reified 3231 (merge-constructs 3232 (reified-construct merged-reifier-topic 3233 :revision revision) construct))) 3234 (setf merged-construct merged-reified))) 3235 ((find construct all-constructs) 3236 (let ((reifier-assoc 3237 (loop for reifier-assoc in 3238 (slot-p merged-reifier-topic 'reified-construct) 3239 when (eql (reifiable-construct reifier-assoc) 3240 construct) 3241 return reifier-assoc))) 3242 (add-to-version-history reifier-assoc 3243 :start-revision revision))) 3244 (t 3245 (make-construct 'ReifierAssociationC 3246 :reifiable-construct construct 3247 :reifier-topic merged-reifier-topic 3248 :start-revision revision))) 3249 (add-version-info construct revision) 3250 merged-construct))))) 3251 3252 3253 (defgeneric private-delete-reifier (construct reifier &key revision) 3254 (:documentation "Sets the association object between the passed constructs 3255 as mark-as-deleted.") 3256 (:method ((construct ReifiableConstructC) (reifier TopicC) 3257 &key (revision (error (make-missing-argument-condition "From private-delete-reifier(): revision must be set" 'revision 'private-delete-reifier)))) 3258 (let ((assoc-to-delete (loop for reifier-assoc in (slot-p construct 'reifier) 3259 when (eql (reifier-topic reifier-assoc) reifier) 3260 return reifier-assoc))) 3261 (when assoc-to-delete 3262 (mark-as-deleted assoc-to-delete :revision revision) 3263 construct)))) 3264 3265 3266 (defgeneric delete-reifier (construct reifier &key revision) 3267 (:documentation "See private-delete-reifier but adds the reified-construct 3268 to the given version.") 3269 (:method ((construct ReifiableConstructC) (reifier TopicC) 3270 &key (revision (error (make-missing-argument-condition "From delete-reifier(): revision must be set" 'revision 'delete-reifier)))) 3271 (when (private-delete-reifier construct reifier :revision revision) 3272 (add-version-info construct revision) 3273 construct))) 3274 3275 3276 (defmethod get-all-identifiers-of-construct ((construct ReifiableConstructC) 3277 &key (revision *TM-REVISION*)) 3278 (declare (integer revision)) 3279 (item-identifiers construct :revision revision)) 3280 3281 3282 ;;; TypableC 3283 (defgeneric TypableC-p (class-symbol) 3284 (:documentation "Returns t if the passed class is equal to TypableC or 3285 one of its subtypes.") 3286 (:method ((class-symbol symbol)) 3287 (or (eql class-symbol 'TypableC) 3288 (AssociationC-p class-symbol) 3289 (RoleC-p class-symbol) 3290 (CharacteristicC-p class-symbol)))) 3291 3292 3293 (defgeneric complete-typable (construct instance-of &key start-revision) 3294 (:documentation "Adds the passed instance-of to the given construct.") 3295 (:method ((construct TypableC) instance-of 3296 &key (start-revision *TM-REVISION*)) 3297 (declare (integer start-revision) (type (or null TopicC) instance-of)) 3298 (when instance-of 3299 (add-type construct instance-of :revision start-revision)) 3300 construct)) 3301 3302 3303 (defgeneric equivalent-typable-construct (construct instance-of 3304 &key start-revision) 3305 (:documentation "Returns t if the passed constructs are TMDM equal, i.e. 3306 the typable constructs have to own the same type.") 3307 (:method ((construct TypableC) instance-of &key (start-revision *TM-REVISION*)) 3308 (declare (integer start-revision) 3309 (type (or null TopicC) instance-of)) 3310 (eql (instance-of construct :revision start-revision) instance-of))) 3311 3312 3313 ;;; ScopableC 3314 (defgeneric ScopableC-p (class-symbol) 3315 (:documentation "Returns t if the passed class is equal to ScopableC or 3316 one of its subtypes.") 3317 (:method ((class-symbol symbol)) 3318 (or (eql class-symbol 'ScopableC) 3319 (AssociationC-p class-symbol) 3320 (CharacteristicC-p class-symbol)))) 3321 3322 3323 (defgeneric complete-scopable (construct themes &key start-revision) 3324 (:documentation "Adds all passed themes to the given construct.") 3325 (:method ((construct ScopableC) (themes list) 3326 &key (start-revision *TM-REVISION*)) 3327 (declare (integer start-revision)) 3328 (dolist (theme themes) 3329 (add-theme construct theme :revision start-revision)) 3330 construct)) 3331 3332 3333 (defgeneric equivalent-scopable-construct (construct themes &key start-revision) 3334 (:documentation "Returns t if the passed constructs are TMDM equal, i.e. 3335 the scopable constructs have to own the same themes.") 3336 (:method ((construct ScopableC) themes &key (start-revision *TM-REVISION*)) 3337 (declare (integer start-revision) (list themes)) 3338 (not (set-exclusive-or (themes construct :revision start-revision) 3339 themes)))) 3340 3341 3342 (defmethod delete-construct :before ((construct ScopableC)) 3343 (dolist (scope-assoc-to-delete (slot-p construct 'themes)) 3344 (delete-construct scope-assoc-to-delete))) 3345 3346 3347 (defgeneric themes (construct &key revision) 3348 (:documentation "Returns all topics that correspond with the given revision 3349 as a scope for the given topic.") 3350 (:method ((construct ScopableC) &key (revision *TM-REVISION*)) 3351 (let ((valid-associations 3352 (filter-slot-value-by-revision construct 'themes 3353 :start-revision revision))) 3354 (map 'list #'theme-topic valid-associations)))) 3355 3356 3357 (defgeneric add-theme (construct theme-topic &key revision) 3358 (:documentation "Adds the given theme-topic to the passed 3359 scopable-construct.") 3360 (:method ((construct ScopableC) (theme-topic TopicC) 3361 &key (revision *TM-REVISION*)) 3362 (let ((all-themes 3363 (map 'list #'theme-topic (slot-p construct 'themes)))) 3364 (if (find theme-topic all-themes) 3365 (let ((theme-assoc 3366 (loop for theme-assoc in (slot-p construct 'themes) 3367 when (eql (theme-topic theme-assoc) theme-topic) 3368 return theme-assoc))) 3369 (add-to-version-history theme-assoc :start-revision revision)) 3370 (make-construct 'ScopeAssociationC 3371 :theme-topic theme-topic 3372 :scopable-construct construct 3373 :start-revision revision))) 3374 (when (typep construct 'VersionedConstructC) 3375 (add-to-version-history construct :start-revision revision)) 3376 construct)) 3377 3378 3379 (defgeneric private-delete-theme (construct theme-topic &key revision) 3380 (:documentation "Deletes the passed theme by marking it's association as 3381 deleted in the passed revision.") 3382 (:method ((construct ScopableC) (theme-topic TopicC) 3383 &key (revision (error (make-missing-argument-condition "From private-delete-theme(): revision must be set" 'revision 'private-delete-theme)))) 3384 (let ((assoc-to-delete (loop for theme-assoc in (slot-p construct 'themes) 3385 when (eql (theme-topic theme-assoc) theme-topic) 3386 return theme-assoc))) 3387 (when assoc-to-delete 3388 (mark-as-deleted assoc-to-delete :revision revision) 3389 construct)))) 3390 3391 3392 (defgeneric delete-theme (construct theme-topic &key revision) 3393 (:documentation "See private-delete-theme but adds the parent construct 3394 to the given version.") 3395 (:method ((construct ScopableC) (theme-topic TopicC) 3396 &key (revision (error (make-missing-argument-condition "From delete-theme(): revision must be set" 'revision 'delete-theme)))) 3397 (when (private-delete-theme construct theme-topic :revision revision) 3398 (add-version-info construct revision) 3399 construct))) 3400 3401 3402 ;;; TypableC 3403 (defmethod delete-construct :before ((construct TypableC)) 3404 (dolist (type-assoc-to-delete (slot-p construct 'instance-of)) 3405 (delete-construct type-assoc-to-delete))) 3406 3407 3408 (defgeneric instance-of-p (construct) 3409 (:documentation "Returns t if there is any type set in this object. 3410 t is also returned if the type is marked-as-deleted.") 3411 (:method ((construct TypableC)) 3412 (when (slot-p construct 'instance-of) 3413 t))) 3414 3415 3416 (defgeneric instance-of (construct &key revision) 3417 (:documentation "Returns the type topic that is set on the passed 3418 revision.") 3419 (:method ((construct TypableC) &key (revision *TM-REVISION*)) 3420 (let ((valid-associations 3421 (filter-slot-value-by-revision construct 'instance-of 3422 :start-revision revision))) 3423 (when valid-associations 3424 (type-topic (first valid-associations)))))) 3425 3426 3427 (defgeneric add-type (construct type-topic &key revision) 3428 (:documentation "Add the passed type-topic as type to the given 3429 typed construct if there is no other type-topic 3430 set at the same revision.") 3431 (:method ((construct TypableC) (type-topic TopicC) 3432 &key (revision *TM-REVISION*)) 3433 (let ((already-set-type (instance-of construct :revision revision)) 3434 (same-type-assoc 3435 (loop for type-assoc in (slot-p construct 'instance-of) 3436 when (eql (type-topic type-assoc) type-topic) 3437 return type-assoc))) 3438 (when (and already-set-type 3439 (not (eql type-topic already-set-type))) 3440 (error (make-tm-reference-condition (format nil "From add-type(): ~a can't be typed by ~a since it is typed by ~a" 3441 construct type-topic already-set-type) 3442 construct (instance-of construct :revision revision) type-topic))) 3443 (cond (already-set-type 3444 (let ((type-assoc 3445 (loop for type-assoc in (slot-p construct 'instance-of) 3446 when (eql type-topic (type-topic type-assoc)) 3447 return type-assoc))) 3448 (add-to-version-history type-assoc :start-revision revision))) 3449 (same-type-assoc 3450 (add-to-version-history same-type-assoc :start-revision revision)) 3451 (t 3452 (make-construct 'TypeAssociationC 3453 :type-topic type-topic 3454 :typable-construct construct 3455 :start-revision revision)))) 3456 (when (typep construct 'VersionedConstructC) 3457 (add-to-version-history construct :start-revision revision)) 3458 construct)) 3459 3460 3461 (defgeneric private-delete-type (construct type-topic &key revision) 3462 (:documentation "Deletes the passed type by marking it's association as 3463 deleted in the passed revision.") 3464 (:method ((construct TypableC) (type-topic TopicC) 3465 &key (revision (error (make-missing-argument-condition "From private-delete-type(): revision must be set" 'revision 'private-delete-type)))) 3466 (let ((assoc-to-delete 3467 (loop for type-assoc in (slot-p construct 'instance-of) 3468 when (eql (type-topic type-assoc) type-topic) 3469 return type-assoc))) 3470 (when assoc-to-delete 3471 (mark-as-deleted assoc-to-delete :revision revision) 3472 construct)))) 3473 3474 3475 (defgeneric delete-type (construct type-topic &key revision) 3476 (:documentation "See private-delete-type but adds the parent construct 3477 to the given version.") 3478 (:method ((construct TypableC) (type-topic TopicC) 3479 &key (revision (error (make-missing-argument-condition "From private-delete-type(): revision must be set" 'revision 'private-delete-type)))) 3480 (when (private-delete-type construct type-topic :revision revision) 3481 (add-version-info construct revision) 3482 construct))) 3483 3484 3485 ;;; TopicMapC 3486 (defmethod equivalent-constructs ((construct-1 TopicMapC) (construct-2 TopicMapC) 3487 &key (revision *TM-REVISION*)) 3488 (declare (integer revision)) 3489 (when (intersection (item-identifiers construct-1 :revision revision) 3490 (item-identifiers construct-2 :revision revision)) 3491 t)) 3492 3493 3494 (defgeneric TopicMapC-p (class-symbol) 3495 (:documentation "Returns t if the passed symbol is equal to TopicMapC.") 3496 (:method ((class-symbol symbol)) 3497 (eql class-symbol 'TopicMapC))) 3498 3499 3500 (defmethod equivalent-construct ((construct TopicMapC) 3501 &key (start-revision *TM-REVISION*) 3502 (reifier nil) (item-identifiers nil)) 3503 "TopicMaps equality if they share the same item-identier or reifier." 3504 (declare (list item-identifiers) (integer start-revision) 3505 (type (or null TopicC) reifier)) 3506 (equivalent-reifiable-construct construct reifier item-identifiers 3507 :start-revision start-revision)) 3508 3509 3510 (defmethod delete-construct :before ((construct TopicMapC)) 3511 (dolist (top (slot-p construct 'topics)) 3512 (remove-association construct 'topics top)) 3513 (dolist (assoc (slot-p construct 'associations)) 3514 (remove-association construct 'associations assoc))) 3515 3516 3517 (defmethod add-to-tm ((construct TopicMapC) (construct-to-add TopicC)) 3518 (add-association construct 'topics construct-to-add) 3519 construct-to-add) 3520 3521 3522 (defmethod add-to-tm ((construct TopicMapC) (construct-to-add AssociationC)) 3523 (add-association construct 'associations construct-to-add) 3524 construct-to-add) 3525 3526 3527 (defmethod delete-from-tm ((construct TopicMapC) (construct-to-delete TopicC)) 3528 (remove-association construct 'topics construct-to-delete)) 3529 3530 3531 (defmethod delete-from-tm ((construct TopicMapC) 3532 (construct-to-delete AssociationC)) 3533 (remove-association construct 'associations construct-to-delete)) 3534 3535 3536 (defgeneric in-topicmap (tm construct &key revision) 3537 (:documentation "Is a given construct (topic or assiciation) in this 3538 topic map?")) 3539 3540 3541 (defmethod in-topicmap ((tm TopicMapC) (top TopicC) &key 3542 (revision *TM-REVISION*)) 1587 3543 (when (find-item-by-revision top revision) 1588 (find (d:internal-id top) (d:topics tm) :test #'= :key #'d:internal-id))) 1589 1590 1591 (defmethod in-topicmap ((tm TopicMapC) (ass AssociationC) &key (revision 0)) 3544 (find (internal-id top) (topics tm) :test #'= :key #'internal-id))) 3545 3546 3547 (defmethod in-topicmap ((tm TopicMapC) (ass AssociationC) 3548 &key (revision *TM-REVISION*)) 1592 3549 (when (find-item-by-revision ass revision) 1593 (find (d:internal-id ass) (d:associations tm) :test #'= :key #'d:internal-id))) 1594 1595 ;;;;;;;;;;;;;;;;; 1596 ;; reification 1597 1598 (defgeneric add-reifier (construct reifier-topic) 1599 (:method ((construct ReifiableConstructC) reifier-topic) 1600 (let ((err "From add-reifier(): ")) 1601 (declare (TopicC reifier-topic)) 1602 (cond 1603 ((and (not (reifier construct)) 1604 (not (reified reifier-topic))) 1605 (setf (reifier construct) reifier-topic) 1606 (setf (reified reifier-topic) construct)) 1607 ((and (not (reified reifier-topic)) 1608 (reifier construct)) 1609 (merge-reifier-topics (reifier construct) reifier-topic)) 1610 ((and (not (reifier construct)) 1611 (reified reifier-topic)) 1612 (error "~a~a ~a reifies already another object ~a" 1613 err (psis reifier-topic) (item-identifiers reifier-topic) 1614 (reified reifier-topic))) 1615 (t 1616 (when (not (eql (reified reifier-topic) construct)) 1617 (error "~a~a ~a reifies already another object ~a" 1618 err (psis reifier-topic) (item-identifiers reifier-topic) 1619 (reified reifier-topic))) 1620 (merge-reifier-topics (reifier construct) reifier-topic))) 1621 construct))) 1622 1623 1624 (defgeneric remove-reifier (construct) 1625 (:method ((construct ReifiableConstructC)) 1626 (let ((reifier-topic (reifier construct))) 1627 (when reifier-topic 1628 (elephant:remove-association construct 'reifier reifier-topic) 1629 (elephant:remove-association reifier-topic 'reified construct))))) 1630 1631 1632 (defgeneric merge-reifier-topics (old-topic new-topic) 1633 ;;the reifier topics are not only merged but also bound to the reified-construct 1634 (:method ((old-topic TopicC) (new-topic TopicC)) 1635 (unless (eql old-topic new-topic) 1636 ;merges all identifiers 1637 (move-identifiers old-topic new-topic) 1638 (move-identifiers old-topic new-topic :what 'locators) 1639 (move-identifiers old-topic new-topic :what 'psis) 1640 (move-identifiers old-topic new-topic :what 'topic-identifiers) 1641 ;merges all typed-object-associations 1642 (dolist (typed-construct (used-as-type new-topic)) 1643 (remove-association typed-construct 'instance-of new-topic) 1644 (add-association typed-construct 'instance-of old-topic)) 1645 ;merges all scope-object-associations 1646 (dolist (scoped-construct (used-as-theme new-topic)) 1647 (remove-association scoped-construct 'themes new-topic) 1648 (add-association scoped-construct 'themes old-topic)) 1649 ;merges all topic-maps 1650 (dolist (tm (in-topicmaps new-topic)) 1651 (add-association tm 'topics old-topic)) ;the new-topic is removed from this tm by deleting it 1652 ;merges all role-players 1653 (dolist (a-role (player-in-roles new-topic)) 1654 (remove-association a-role 'player new-topic) 1655 (add-association a-role 'player old-topic)) 1656 ;merges all names 1657 (dolist (name (names new-topic)) 1658 (remove-association name 'topic new-topic) 1659 (add-association name 'topic old-topic)) 1660 ;merges all occurrences 1661 (dolist (occurrence (occurrences new-topic)) 1662 (remove-association occurrence 'topic new-topic) 1663 (add-association occurrence 'topic old-topic)) 1664 ;merges all version-infos 1665 (let ((versions-to-move 1666 (loop for vrs in (versions new-topic) 1667 when (not (find-if #'(lambda(x) 1668 (and (= (start-revision x) (start-revision vrs)) 1669 (= (end-revision x) (end-revision vrs)))) 1670 (versions old-topic))) 1671 collect vrs))) 1672 (dolist (vrs versions-to-move) 1673 (remove-association vrs 'versioned-construct new-topic) 1674 (add-association vrs 'versioned-construct old-topic))) 1675 (delete-construct new-topic)) 1676 ;TODO: order/repair all version-infos of the topic itself and add all new 1677 ; versions to the original existing objects of the topic 1678 old-topic)) 3550 (find (internal-id ass) (associations tm) :test #'= :key #'internal-id))) 3551 3552 3553 ;;; make-construct ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 3554 (defun make-construct (class-symbol &rest args) 3555 "Creates a new topic map construct if necessary or 3556 retrieves an equivalent one if available and updates the revision 3557 history accordingly. Returns the object in question. Methods use 3558 specific keyword arguments for their purpose." 3559 (declare (symbol class-symbol)) 3560 (when (and (or (VersionedConstructC-p class-symbol) 3561 (and (ReifiableConstructC-p class-symbol) 3562 (or (getf args :item-identifiers) (getf args :reifier)))) 3563 (not (getf args :start-revision))) 3564 (error (make-missing-argument-condition "From make-construct(): start-revision must be set" 'start-revision 'make-construct))) 3565 (let ((construct 3566 (cond 3567 ((PointerC-p class-symbol) 3568 (apply #'make-pointer class-symbol args)) 3569 ((CharacteristicC-p class-symbol) 3570 (apply #'make-characteristic class-symbol args)) 3571 ((TopicC-p class-symbol) 3572 (apply #'make-topic args)) 3573 ((TopicMapC-p class-symbol) 3574 (apply #'make-tm args)) 3575 ((RoleC-p class-symbol) 3576 (apply #'make-role args)) 3577 ((AssociationC-p class-symbol) 3578 (apply #'make-association args)) 3579 ((VersionedConstructC-p class-symbol) 3580 (apply #'make-instance class-symbol 3581 (rec-remf args :start-revision))) 3582 (t 3583 (apply #'make-instance class-symbol args)))) 3584 (start-revision (or (getf args :start-revision) *TM-REVISION*))) 3585 (when (typep construct 'TypableC) 3586 (complete-typable construct (getf args :instance-of) 3587 :start-revision start-revision)) 3588 (when (typep construct 'ScopableC) 3589 (complete-scopable construct (getf args :themes) 3590 :start-revision start-revision)) 3591 (when (typep construct 'VersionedConstructC) 3592 (add-to-version-history construct :start-revision start-revision)) 3593 (when (or (typep construct 'TopicC) (typep construct 'AssociationC)) 3594 (dolist (tm (getf args :in-topicmaps)) 3595 (add-to-tm tm construct))) 3596 (if (typep construct 'ReifiableConstructC) 3597 (complete-reifiable construct (getf args :item-identifiers) 3598 (getf args :reifier) :start-revision start-revision) 3599 construct))) 3600 3601 3602 (defun make-association (&rest args) 3603 "Returns an association object. If the association has already existed the 3604 existing one is returned otherwise a new one is created. 3605 This function exists only for being used by make-construct!" 3606 (let ((instance-of (getf args :instance-of)) 3607 (start-revision (getf args :start-revision)) 3608 (themes (getf args :themes)) 3609 (roles (getf args :roles))) 3610 (when (and (or roles instance-of themes) 3611 (not start-revision)) 3612 (error (make-missing-argument-condition "From make-association(): start-revision must be set" 'start-revision 'make-association))) 3613 (let ((association 3614 (let ((existing-associations 3615 (remove-if 3616 #'null 3617 (map 'list #'(lambda(existing-association) 3618 (when (equivalent-construct 3619 existing-association 3620 :start-revision start-revision 3621 :roles roles :themes themes 3622 :instance-of instance-of) 3623 existing-association)) 3624 (get-all-associations nil))))) 3625 (cond ((> (length existing-associations) 1) 3626 (merge-all-constructs existing-associations 3627 :revision start-revision)) 3628 (existing-associations 3629 (first existing-associations)) 3630 (t 3631 (make-instance 'AssociationC)))))) 3632 (dolist (role-plist roles) 3633 (add-role association 3634 (apply #'make-construct 'RoleC 3635 (append role-plist (list :parent association))) 3636 :revision (getf role-plist :start-revision))) 3637 association))) 3638 3639 3640 (defun make-role (&rest args) 3641 "Returns a role object. If the role has already existed the 3642 existing one is returned otherwise a new one is created. 3643 This function exists only for being used by make-construct!" 3644 (let ((parent (getf args :parent)) 3645 (instance-of (getf args :instance-of)) 3646 (player (getf args :player)) 3647 (start-revision (getf args :start-revision))) 3648 (when (and (or instance-of player parent) 3649 (not start-revision)) 3650 (error (make-missing-argument-condition "From make-role(): start-revision must be set" 'start-revision 'make-role))) 3651 (let ((role 3652 (let ((existing-roles 3653 (when parent 3654 (remove-if 3655 #'null 3656 (map 'list #'(lambda(existing-role) 3657 (when (equivalent-construct 3658 existing-role 3659 :start-revision start-revision 3660 :player player 3661 :instance-of instance-of) 3662 existing-role)) 3663 (map 'list #'role (slot-p parent 'roles))))))) 3664 (if (and existing-roles 3665 (or (eql parent (parent (first existing-roles) 3666 :revision start-revision)) 3667 (not (parent (first existing-roles) 3668 :revision start-revision)))) 3669 (progn 3670 (add-role parent (first existing-roles) 3671 :revision start-revision) 3672 (first existing-roles)) 3673 (make-instance 'RoleC))))) 3674 (when player 3675 (add-player role player :revision start-revision)) 3676 (when parent 3677 (add-parent role parent :revision start-revision)) 3678 role))) 3679 3680 3681 (defun make-tm (&rest args) 3682 "Returns a topic map object. If the topic map has already existed the 3683 existing one is returned otherwise a new one is created. 3684 This function exists only for being used by make-construct!" 3685 (let ((item-identifiers (getf args :item-identifiers)) 3686 (reifier (getf args :reifier)) 3687 (topics (getf args :topics)) 3688 (assocs (getf args :associations)) 3689 (start-revision (getf args :start-revision))) 3690 (when (and (or item-identifiers reifier) 3691 (not start-revision)) 3692 (error (make-missing-argument-condition "From make-tm(): start-revision must be set" 'start-revision 'make-tm))) 3693 (let ((tm 3694 (let ((existing-tms 3695 (remove-if 3696 #'null 3697 (map 'list #'(lambda(existing-tm) 3698 (when (equivalent-construct 3699 existing-tm 3700 :item-identifiers item-identifiers 3701 :reifier reifier) 3702 existing-tm)) 3703 (get-all-tms start-revision))))) 3704 (cond ((> (length existing-tms) 1) 3705 (merge-all-constructs existing-tms :revision start-revision)) 3706 (existing-tms 3707 (first existing-tms)) 3708 (t 3709 (make-instance 'TopicMapC)))))) 3710 (dolist (top-or-assoc (union topics assocs)) 3711 (add-to-tm tm top-or-assoc)) 3712 tm))) 3713 3714 3715 (defun make-topic (&rest args) 3716 "Returns a topic object. If the topic has already existed the existing one is 3717 returned otherwise a new one is created. 3718 This function exists only for being used by make-construct!" 3719 (let ((start-revision (getf args :start-revision)) 3720 (psis (getf args :psis)) 3721 (locators (getf args :locators)) 3722 (item-identifiers (getf args :item-identifiers)) 3723 (topic-identifiers (getf args :topic-identifiers)) 3724 (names (getf args :names)) 3725 (occurrences (getf args :occurrences)) 3726 (reified-construct (getf args :refied-construct))) 3727 (when (and (or psis locators item-identifiers topic-identifiers 3728 names occurrences) 3729 (not start-revision)) 3730 (error (make-missing-argument-condition "From make-topic(): start-revision must be set" 'start-revision 'make-topic))) 3731 (let ((topic 3732 (let ((existing-topics 3733 (remove-if 3734 #'null 3735 (map 'list #'(lambda(existing-topic) 3736 (when (equivalent-construct 3737 existing-topic 3738 :start-revision start-revision 3739 :psis psis :locators locators 3740 :item-identifiers item-identifiers 3741 :topic-identifiers topic-identifiers) 3742 existing-topic)) 3743 (get-all-topics start-revision))))) 3744 (cond ((> (length existing-topics) 1) 3745 (merge-all-constructs existing-topics :revision start-revision)) 3746 (existing-topics 3747 (first existing-topics)) 3748 (t 3749 (make-instance 'TopicC)))))) 3750 (let ((merged-topic topic)) 3751 (dolist (tid topic-identifiers) 3752 (setf merged-topic (add-topic-identifier merged-topic tid 3753 :revision start-revision))) 3754 (dolist (psi psis) 3755 (setf merged-topic (add-psi merged-topic psi 3756 :revision start-revision))) 3757 (dolist (locator locators) 3758 (setf merged-topic (add-locator merged-topic locator 3759 :revision start-revision))) 3760 (dolist (name names) 3761 (setf merged-topic (add-name merged-topic name 3762 :revision start-revision))) 3763 (dolist (occ occurrences) 3764 (add-occurrence merged-topic occ :revision start-revision)) 3765 (when reified-construct 3766 (add-reified-construct merged-topic reified-construct 3767 :revision start-revision)) 3768 merged-topic)))) 3769 3770 3771 (defun make-characteristic (class-symbol &rest args) 3772 "Returns a characteristic object with the passed parameters. 3773 If an equivalent construct has already existed this one is returned. 3774 To check if there is existing an equivalent construct the parameter 3775 parent-construct must be set. 3776 This function only exists for being used by make-construct!" 3777 (let ((charvalue (or (getf args :charvalue) "")) 3778 (start-revision (getf args :start-revision)) 3779 (datatype (or (getf args :datatype) *xml-string*)) 3780 (instance-of (getf args :instance-of)) 3781 (themes (getf args :themes)) 3782 (variants (getf args :variants)) 3783 (parent (getf args :parent))) 3784 (when (and (or instance-of themes variants parent) 3785 (not start-revision)) 3786 (error (make-missing-argument-condition "From make-characteristic(): start-revision must be set" 'start-revision 'make-characgteristic))) 3787 (let ((characteristic 3788 (let ((existing-characteristics 3789 (when parent 3790 (remove-if 3791 #'null 3792 (map 'list #'(lambda(existing-characteristic) 3793 (when (equivalent-construct 3794 existing-characteristic 3795 :start-revision start-revision 3796 :datatype datatype :variants variants 3797 :charvalue charvalue :themes themes 3798 :instance-of instance-of) 3799 existing-characteristic)) 3800 (get-all-characteristics parent class-symbol)))))) 3801 (if (and existing-characteristics 3802 (or (eql parent (parent (first existing-characteristics) 3803 :revision start-revision)) 3804 (not (parent (first existing-characteristics) 3805 :revision start-revision)))) 3806 (progn 3807 (add-characteristic parent (first existing-characteristics) 3808 :revision start-revision) 3809 (first existing-characteristics)) 3810 (make-instance class-symbol :charvalue charvalue 3811 :datatype datatype))))) 3812 (when (typep characteristic 'NameC) 3813 (complete-name characteristic variants :start-revision start-revision)) 3814 (when parent 3815 (add-parent characteristic parent :revision start-revision)) 3816 characteristic))) 3817 3818 3819 (defun make-pointer (class-symbol &rest args) 3820 "Returns a pointer object with the specified parameters. 3821 If an equivalen construct has already existed this one is returned. 3822 This function only exists for beoing used by make-construct!" 3823 (let ((uri (getf args :uri)) 3824 (xtm-id (getf args :xtm-id)) 3825 (start-revision (getf args :start-revision)) 3826 (identified-construct (getf args :identified-construct)) 3827 (err "From make-pointer(): ")) 3828 (when (and identified-construct (not start-revision)) 3829 (error (make-missing-argument-condition (format nil "~astart-revision must be set" err) 'start-revision 'make-pointer))) 3830 (unless uri 3831 (error (make-missing-argument-condition (format nil "~auri must be set" err) 'uri 'make-pointer))) 3832 (when (and (TopicIdentificationC-p class-symbol) 3833 (not xtm-id)) 3834 (error (make-missing-argument-condition (format nil "~axtm-id must be set" err) 'xtm-id 'make-pointer))) 3835 (let ((identifier 3836 (let ((existing-pointer 3837 (remove-if 3838 #'null 3839 (map 'list 3840 #'(lambda(existing-pointer) 3841 (when (and (typep existing-pointer class-symbol) 3842 (equivalent-construct existing-pointer 3843 :uri uri 3844 :xtm-id xtm-id)) 3845 existing-pointer)) 3846 (elephant:get-instances-by-value class-symbol 'd::uri uri))))) 3847 (if existing-pointer 3848 (first existing-pointer) 3849 (make-instance class-symbol :uri uri :xtm-id xtm-id))))) 3850 (when identified-construct 3851 (cond ((TopicIdentificationC-p class-symbol) 3852 (add-topic-identifier identified-construct identifier 3853 :revision start-revision)) 3854 ((PersistentIdC-p class-symbol) 3855 (add-psi identified-construct identifier :revision start-revision)) 3856 ((ItemIdentifierC-p class-symbol) 3857 (add-item-identifier identified-construct identifier 3858 :revision start-revision)) 3859 ((SubjectLocatorC-p class-symbol) 3860 (add-locator identified-construct identifier 3861 :revision start-revision)))) 3862 identifier))) 3863 3864 3865 ;;; merge-constructs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 3866 (defgeneric move-identifiers (source destination &key revision) 3867 (:documentation "Sets all identifiers as mark as deleted in the given 3868 version and adds the marked identifiers to the 3869 destination construct.")) 3870 3871 3872 (defmethod move-identifiers ((source ReifiableConstructC) 3873 (destination ReifiableConstructC) 3874 &key (revision *TM-REVISION*)) 3875 (declare (integer revision)) 3876 (let ((iis (item-identifiers source :revision revision))) 3877 (dolist (ii iis) 3878 (private-delete-item-identifier source ii :revision revision) 3879 (add-item-identifier destination ii :revision revision)) 3880 iis)) 3881 3882 3883 (defmethod move-identifiers ((source TopicC) (destination TopicC) 3884 &key (revision *TM-REVISION*)) 3885 (declare (integer revision)) 3886 (let ((iis (call-next-method)) 3887 (tids (topic-identifiers source :revision revision)) 3888 (psis (psis source :revision revision)) 3889 (sls (locators source :revision revision))) 3890 (dolist (tid tids) 3891 (private-delete-topic-identifier source tid :revision revision) 3892 (add-topic-identifier destination tid :revision revision)) 3893 (dolist (psi psis) 3894 (private-delete-psi source psi :revision revision) 3895 (add-psi destination psi :revision revision)) 3896 (dolist (sl sls) 3897 (private-delete-locator source sl :revision revision) 3898 (add-locator destination sl :revision revision)) 3899 (append tids iis psis sls))) 3900 3901 3902 (defgeneric move-referenced-constructs (source destination &key revision) 3903 (:documentation "Moves all referenced constructs in the given version from 3904 the source TM-construct to the destination TM-construct.")) 3905 3906 3907 (defmethod move-referenced-constructs ((source ReifiableConstructC) 3908 (destination ReifiableConstructC) 3909 &key (revision *TM-REVISION*)) 3910 (declare (integer revision)) 3911 (remove-if 3912 #'null 3913 (append 3914 (move-identifiers source destination :revision revision) 3915 (let ((source-reifier (reifier source :revision revision)) 3916 (destination-reifier (reifier destination :revision revision))) 3917 (let ((result 3918 (cond ((and source-reifier destination-reifier) 3919 (private-delete-reifier (reified-construct source-reifier 3920 :revision revision) 3921 source-reifier :revision revision) 3922 (private-delete-reifier (reified-construct destination-reifier 3923 :revision revision) 3924 destination-reifier :revision revision) 3925 (let ((merged-reifier 3926 (merge-constructs source-reifier destination-reifier 3927 :revision revision))) 3928 (add-reifier destination merged-reifier :revision revision) 3929 merged-reifier)) 3930 (source-reifier 3931 (private-delete-reifier (reified-construct source-reifier 3932 :revision revision) 3933 source-reifier :revision revision) 3934 (add-reifier destination source-reifier :revision revision) 3935 source-reifier) 3936 (destination-reifier 3937 (add-reifier destination destination-reifier :revision revision) 3938 nil)))) 3939 (when result 3940 (list result))))))) 3941 3942 3943 (defmethod move-referenced-constructs ((source NameC) (destination NameC) 3944 &key (revision *TM-REVISION*)) 3945 (declare (integer revision)) 3946 (append (call-next-method) 3947 (move-variants source destination :revision revision))) 3948 3949 3950 (defmethod move-referenced-constructs ((source TopicC) (destination TopicC) 3951 &key (revision *TM-REVISION*)) 3952 (let ((roles (player-in-roles source :revision revision)) 3953 (scopables (used-as-theme source :revision revision)) 3954 (typables (used-as-type source :revision revision)) 3955 (ids (move-identifiers source destination :revision revision))) 3956 (dolist (role roles) 3957 (private-delete-player role source :revision revision) 3958 (add-player role destination :revision revision)) 3959 (dolist (scopable scopables) 3960 (private-delete-theme scopable source :revision revision) 3961 (add-theme scopable destination :revision revision)) 3962 (dolist (typable typables) 3963 (private-delete-type typable source :revision revision) 3964 (add-type typable destination :revision revision)) 3965 (remove-if #'null (append roles scopables typables ids)))) 3966 3967 3968 (defgeneric move-reified-construct (source destination &key revision) 3969 (:documentation "Moves the refied TM-construct from the source topic 3970 to the given destination topic.") 3971 (:method ((source TopicC) (destination TopicC) &key (revision *TM-REVISION*)) 3972 (declare (integer revision)) 3973 (let ((source-reified (reified-construct source :revision revision)) 3974 (destination-reified (reified-construct destination 3975 :revision revision))) 3976 (when (and source-reified destination-reified 3977 (not (eql (type-of source-reified) 3978 (type-of destination-reified)))) 3979 (error (make-not-mergable-condition (format nil "From move-reified-construct(): ~a and ~a can't be merged since the reified-constructs are not of the same type ~a ~a" 3980 source destination source-reified destination-reified) 3981 source destination))) 3982 (cond ((and source-reified destination-reified) 3983 (private-delete-reifier source-reified source :revision revision) 3984 (private-delete-reifier destination-reified destination :revision revision) 3985 (let ((merged-reified 3986 (merge-constructs source-reified destination-reified 3987 :revision revision))) 3988 (add-reifier merged-reified destination :revision revision) 3989 merged-reified)) 3990 (source-reified 3991 (private-delete-reifier source source-reified :revision revision) 3992 (add-reifier source-reified destination :revision revision) 3993 source-reified) 3994 (destination-reified 3995 (add-reifier destination-reified destination :revision revision) 3996 destination-reified))))) 3997 3998 3999 (defgeneric move-occurrences (source destination &key revision) 4000 (:documentation "Moves all occurrences from the source topic to the 4001 destination topic. If occurrences are TMDM equal 4002 they are merged, i.e. one is marked-as-deleted.") 4003 (:method ((source TopicC) (destination TopicC) &key (revision *TM-REVISION*)) 4004 (declare (integer revision)) 4005 (let ((occs-to-move (occurrences source :revision revision))) 4006 (dolist (occ occs-to-move) 4007 (private-delete-occurrence source occ :revision revision) 4008 (let ((equivalent-occ 4009 (find-if #'(lambda (destination-occ) 4010 (when 4011 (strictly-equivalent-constructs 4012 occ destination-occ :revision revision) 4013 destination-occ)) 4014 (occurrences destination :revision revision)))) 4015 (if equivalent-occ 4016 (progn 4017 (add-occurrence destination equivalent-occ :revision revision) 4018 (move-referenced-constructs occ equivalent-occ 4019 :revision revision)) 4020 (add-occurrence destination occ :revision revision)))) 4021 occs-to-move))) 4022 4023 4024 (defgeneric move-variants (source destination &key revision) 4025 (:documentation "Moves all variants from the source name to the destination 4026 name. If any variants are TMDM equal they are merged --> 4027 i.e. one of the variants is marked-as-deleted.") 4028 (:method ((source NameC) (destination NameC) &key (revision *TM-REVISION*)) 4029 (declare (integer revision)) 4030 (let ((vars-to-move (variants source :revision revision))) 4031 (dolist (var vars-to-move) 4032 (private-delete-variant source var :revision revision) 4033 (let ((equivalent-var 4034 (find-if #'(lambda (destination-var) 4035 (when 4036 (strictly-equivalent-constructs 4037 var destination-var :revision revision) 4038 destination-var)) 4039 (variants destination :revision revision)))) 4040 (if equivalent-var 4041 (progn 4042 (add-variant destination equivalent-var :revision revision) 4043 (move-referenced-constructs var equivalent-var 4044 :revision revision)) 4045 (add-variant destination var :revision revision)))) 4046 vars-to-move))) 4047 4048 4049 (defgeneric move-names (source destination &key revision) 4050 (:documentation "Moves all names from the source topic to the destination 4051 topic. If any names are equal they are merged, i.e. 4052 one of the names is marked-as-deleted.") 4053 (:method ((source TopicC) (destination TopicC) &key (revision *TM-REVISION*)) 4054 (declare (integer revision)) 4055 (let ((names-to-move (names source :revision revision))) 4056 (dolist (name names-to-move) 4057 (private-delete-name source name :revision revision) 4058 (let ((equivalent-name 4059 (find-if #'(lambda (destination-name) 4060 (when 4061 (strictly-equivalent-constructs 4062 name destination-name :revision revision) 4063 destination-name)) 4064 (names destination :revision revision)))) 4065 (if equivalent-name 4066 (progn 4067 (add-name destination equivalent-name :revision revision) 4068 (move-referenced-constructs name equivalent-name 4069 :revision revision)) 4070 (add-name destination name :revision revision)))) 4071 names-to-move))) 4072 4073 4074 (defun merge-changed-constructs (older-topic &key (revision *TM-REVISION*)) 4075 (declare (TopicC older-topic)) 4076 (dolist (construct (append (used-as-type older-topic :revision revision) 4077 (used-as-theme older-topic :revision revision) 4078 (player-in-roles older-topic :revision revision))) 4079 (let ((parent (when (or (typep construct 'RoleC) 4080 (typep construct 'CharacteristicC)) 4081 (parent construct :revision revision)))) 4082 (let ((all-other (cond ((typep construct 'OccurrenceC) 4083 (occurrences parent :revision revision)) 4084 ((typep construct 'NameC) 4085 (names parent :revision revision)) 4086 ((typep construct 'VariantC) 4087 (variants parent :revision revision)) 4088 ((typep construct 'RoleC) 4089 (roles parent :revision revision))))) 4090 (let ((all-equivalent 4091 (remove-if 4092 #'null 4093 (map 'list #'(lambda(other) 4094 (when (strictly-equivalent-constructs 4095 construct other :revision revision) 4096 other)) 4097 all-other)))) 4098 (when all-equivalent 4099 (merge-all-constructs (append all-equivalent (list construct)) 4100 :revision revision)))))) 4101 (merge-changed-associations older-topic :revision revision)) 4102 4103 4104 (defun merge-changed-associations (older-topic &key (revision *TM-REVISION*)) 4105 "Merges all associations that became TMDM-equal since two referenced topics 4106 were merged, e.g. the association types." 4107 (declare (TopicC older-topic)) 4108 (let ((all-assocs 4109 (remove-duplicates 4110 (append 4111 (remove-if 4112 #'null 4113 (map 'list #'(lambda(role) 4114 (parent role :revision revision)) 4115 (player-in-roles older-topic :revision revision))) 4116 (remove-if 4117 #'null 4118 (map 4119 'list #'(lambda(constr) 4120 (when (typep constr 'AssociationC) 4121 constr)) 4122 (append (used-as-type older-topic :revision revision) 4123 (used-as-theme older-topic :revision revision)))))))) 4124 (dolist (assoc all-assocs) 4125 (let ((all-equivalent 4126 (remove-if 4127 #'null 4128 (map 'list #'(lambda(db-assoc) 4129 (when (strictly-equivalent-constructs 4130 assoc db-assoc :revision revision) 4131 db-assoc)) 4132 (get-all-associations nil))))) 4133 (when all-equivalent 4134 (merge-all-constructs (append all-equivalent (list assoc)) 4135 :revision revision)))))) 4136 4137 4138 (defmethod merge-constructs ((construct-1 TopicC) (construct-2 TopicC) 4139 &key (revision *TM-REVISION*)) 4140 (if (eql construct-1 construct-2) 4141 construct-1 4142 (let ((older-topic (find-oldest-construct construct-1 construct-2))) 4143 (let ((newer-topic (if (eql older-topic construct-1) 4144 construct-2 4145 construct-1))) 4146 (dolist (tm (in-topicmaps newer-topic :revision revision)) 4147 (add-to-tm tm older-topic)) 4148 (move-names newer-topic older-topic :revision revision) 4149 (move-occurrences newer-topic older-topic :revision revision) 4150 (move-referenced-constructs newer-topic older-topic :revision revision) 4151 (move-reified-construct newer-topic older-topic :revision revision) 4152 (merge-changed-constructs older-topic :revision revision) 4153 (mark-as-deleted newer-topic :revision revision :source-locator nil) 4154 (when (exist-in-version-history-p newer-topic) 4155 (delete-construct newer-topic)) 4156 older-topic)))) 4157 4158 4159 (defmethod merge-constructs ((construct-1 CharacteristicC) 4160 (construct-2 CharacteristicC) 4161 &key (revision *TM-REVISION*)) 4162 (declare (integer revision)) 4163 (if (eql construct-1 construct-2) 4164 construct-1 4165 (let ((older-char (find-oldest-construct construct-1 construct-2))) 4166 (let ((newer-char (if (eql older-char construct-1) 4167 construct-2 4168 construct-1))) 4169 (let ((parent-1 (parent older-char :revision revision)) 4170 (parent-2 (parent newer-char :revision revision))) 4171 (unless (strictly-equivalent-constructs construct-1 construct-2 4172 :revision revision) 4173 (error (make-not-mergable-condition (format nil "From merge-constructs(): ~a and ~a are not mergable" construct-1 construct-2) 4174 construct-1 construct-2))) 4175 (cond ((and parent-1 (eql parent-1 parent-2)) 4176 (move-referenced-constructs newer-char older-char 4177 :revision revision) 4178 (private-delete-characteristic parent-2 newer-char 4179 :revision revision) 4180 (let ((c-assoc 4181 (find-if 4182 #'(lambda(c-assoc) 4183 (and (eql (characteristic c-assoc) older-char) 4184 (eql (parent-construct c-assoc) parent-1))) 4185 (cond ((typep older-char 'OccurrenceC) 4186 (slot-p parent-1 'occurrences)) 4187 ((typep older-char 'NameC) 4188 (slot-p parent-1 'names)) 4189 ((typep older-char 'VariantC) 4190 (slot-p parent-1 'variants)))))) 4191 (add-to-version-history c-assoc :start-revision revision)) 4192 older-char) 4193 ((and parent-1 parent-2) 4194 (let ((active-parent (merge-constructs parent-1 parent-2 4195 :revision revision))) 4196 (let ((found-older-char 4197 (cond ((typep older-char 'OccurrenceC) 4198 (find older-char 4199 (occurrences 4200 active-parent :revision revision))) 4201 ((typep older-char 'NameC) 4202 (find older-char 4203 (names 4204 active-parent :revision revision))) 4205 ((typep older-char 'VariantC) 4206 (find-if 4207 #'(lambda(name) 4208 (find older-char 4209 (variants name 4210 :revision revision))) 4211 (if (parent active-parent :revision revision) 4212 (names (parent active-parent :revision revision) 4213 :revision revision) 4214 (list active-parent))))))) 4215 (if found-older-char 4216 older-char 4217 newer-char)))) 4218 ((or parent-1 parent-2) 4219 (let ((dst (if parent-1 older-char newer-char)) 4220 (src (if parent-1 newer-char older-char))) 4221 (move-referenced-constructs src dst :revision revision) 4222 (delete-if-not-referenced src) 4223 dst)) 4224 (t 4225 (move-referenced-constructs newer-char older-char 4226 :revision revision) 4227 (delete-if-not-referenced newer-char) 4228 older-char))))))) 4229 4230 4231 (defmethod merge-constructs ((construct-1 TopicMapC) (construct-2 TopicMapC) 4232 &key (revision *TM-REVISION*)) 4233 (declare (integer revision)) 4234 (if (eql construct-1 construct-2) 4235 construct-1 4236 (let ((older-tm (find-oldest-construct construct-1 construct-2))) 4237 (let ((newer-tm (if (eql older-tm construct-1) 4238 construct-2 4239 construct-1))) 4240 (move-referenced-constructs newer-tm older-tm :revision revision) 4241 (dolist (top-or-assoc (append (topics newer-tm) (associations newer-tm))) 4242 (add-to-tm older-tm top-or-assoc)) 4243 (add-to-version-history older-tm :start-revision revision) 4244 (mark-as-deleted newer-tm :revision revision) 4245 (when (exist-in-version-history-p newer-tm) 4246 (delete-construct newer-tm)) 4247 older-tm)))) 4248 4249 4250 (defmethod merge-constructs ((construct-1 AssociationC) (construct-2 AssociationC) 4251 &key revision) 4252 (declare (integer revision)) 4253 (if (eql construct-1 construct-2) 4254 construct-1 4255 (let ((older-assoc (find-oldest-construct construct-1 construct-2))) 4256 (let ((newer-assoc (if (eql older-assoc construct-1) 4257 construct-2 4258 construct-1))) 4259 ;(unless (strictly-equivalent-constructs construct-1 construct-2 4260 ; :revision revision) 4261 ;;associations that have different roles can be although merged, e.g. 4262 ;;two roles are in two different association objects references 4263 ;;the same item-identifier or reifier 4264 (when (or (set-exclusive-or (themes construct-1 :revision revision) 4265 (themes construct-2 :revision revision)) 4266 (not (eql (instance-of construct-1 :revision revision) 4267 (instance-of construct-2 :revision revision)))) 4268 (error (make-not-mergable-condition (format nil "From merge-constructs(): ~a and ~a are not mergable" construct-1 construct-2) 4269 construct-1 construct-2))) 4270 (dolist (tm (in-topicmaps newer-assoc :revision revision)) 4271 (add-to-tm tm older-assoc)) 4272 (private-delete-type newer-assoc (instance-of newer-assoc :revision revision) 4273 :revision revision) 4274 (move-referenced-constructs newer-assoc older-assoc) 4275 (dolist (newer-role (roles newer-assoc :revision revision)) 4276 (let ((equivalent-role 4277 (find-if #'(lambda(older-role) 4278 (strictly-equivalent-constructs 4279 older-role newer-role :revision revision)) 4280 (roles older-assoc :revision revision)))) 4281 (when equivalent-role 4282 (move-referenced-constructs newer-role equivalent-role 4283 :revision revision)) 4284 (private-delete-role newer-assoc newer-role :revision revision) 4285 (add-role older-assoc (if equivalent-role 4286 equivalent-role 4287 newer-role) 4288 :revision revision))) 4289 (mark-as-deleted newer-assoc :revision revision) 4290 (when (exist-in-version-history-p newer-assoc) 4291 (delete-construct newer-assoc)) 4292 older-assoc)))) 4293 4294 4295 (defmethod merge-constructs ((construct-1 RoleC) (construct-2 RoleC) 4296 &key (revision *TM-REVISION*)) 4297 (declare (integer *TM-REVISION*)) 4298 (if (eql construct-1 construct-2) 4299 construct-1 4300 (let ((older-role (find-oldest-construct construct-1 construct-2))) 4301 (let ((newer-role (if (eql older-role construct-1) 4302 construct-2 4303 construct-1))) 4304 (unless (strictly-equivalent-constructs construct-1 construct-2 4305 :revision revision) 4306 (error (make-not-mergable-condition (format nil "From merge-constructs(): ~a and ~a are not mergable" construct-1 construct-2) 4307 construct-1 construct-2))) 4308 (let ((parent-1 (parent older-role :revision revision)) 4309 (parent-2 (parent newer-role :revision revision))) 4310 (cond ((and parent-1 (eql parent-1 parent-2)) 4311 (move-referenced-constructs newer-role older-role 4312 :revision revision) 4313 (private-delete-role parent-2 newer-role :revision revision) 4314 (let ((r-assoc 4315 (find-if 4316 #'(lambda(r-assoc) 4317 (and (eql (role r-assoc) older-role) 4318 (eql (parent-construct r-assoc) parent-1))) 4319 (slot-p parent-1 'roles)))) 4320 (add-to-version-history r-assoc :start-revision revision) 4321 older-role)) 4322 ((and parent-1 parent-2) 4323 (let ((active-assoc (merge-constructs parent-1 parent-2 4324 :revision revision))) 4325 (if (find older-role (roles active-assoc 4326 :revision revision)) 4327 older-role 4328 newer-role))) 4329 ((or parent-1 parent-2) 4330 (let ((dst (if parent-1 older-role newer-role)) 4331 (src (if parent-1 newer-role older-role))) 4332 (move-referenced-constructs src dst :revision revision) 4333 (delete-if-not-referenced src) 4334 dst)) 4335 (t 4336 (move-referenced-constructs newer-role older-role 4337 :revision revision) 4338 (delete-if-not-referenced newer-role) 4339 older-role))))))) 4340 4341 4342 (defmethod merge-if-equivalent ((new-role RoleC) (parent-construct AssociationC) 4343 &key (revision *TM-REVISION*)) 4344 (declare (integer revision)) 4345 (let ((possible-roles 4346 (remove-if #'(lambda(role) 4347 (when (parent role :revision revision) 4348 role)) 4349 (map 'list #'role (slot-p parent-construct 'roles))))) 4350 (let ((equivalent-role 4351 (remove-if 4352 #'null 4353 (map 'list 4354 #'(lambda(role) 4355 (when 4356 (strictly-equivalent-constructs role new-role 4357 :revision revision) 4358 role)) 4359 possible-roles)))) 4360 (when equivalent-role 4361 (merge-constructs (first equivalent-role) new-role 4362 :revision revision))))) 4363 4364 4365 (defmethod merge-if-equivalent ((new-characteristic CharacteristicC) 4366 (parent-construct ReifiableConstructC) 4367 &key (revision *TM-REVISION*)) 4368 (declare (integer revision) (type (or TopicC NameC) parent-construct)) 4369 (let ((all-existing-characteristics 4370 (map 'list #'characteristic 4371 (cond ((typep new-characteristic 'OccurrenceC) 4372 (slot-p parent-construct 'occurrences)) 4373 ((typep new-characteristic 'NameC) 4374 (slot-p parent-construct 'names)) 4375 ((typep new-characteristic 'VariantC) 4376 (slot-p parent-construct 'variants)))))) 4377 (let ((possible-characteristics ;all characteristics that are not referenced 4378 ;other constructs at the given revision 4379 (remove-if #'(lambda(char) 4380 (parent char :revision revision)) 4381 all-existing-characteristics))) 4382 (let ((equivalent-construct 4383 (remove-if 4384 #'null 4385 (map 'list 4386 #'(lambda(char) 4387 (when 4388 (strictly-equivalent-constructs char new-characteristic 4389 :revision revision) 4390 char)) 4391 possible-characteristics)))) 4392 (when equivalent-construct 4393 (merge-constructs (first equivalent-construct) new-characteristic 4394 :revision revision)))))) -
TabularUnified trunk/src/model/exceptions.lisp ¶
r26 r325 14 14 :no-identifier-error 15 15 :duplicate-identifier-error 16 :object-not-found-error)) 16 :object-not-found-error 17 :not-mergable-error 18 :missing-argument-error 19 :tm-reference-error)) 17 20 18 21 (in-package :exceptions) … … 22 25 :initarg :message 23 26 :accessor message))) 27 24 28 25 29 (define-condition missing-reference-error(error) … … 32 36 (:documentation "thrown is a reference is missing")) 33 37 38 34 39 (define-condition duplicate-identifier-error(error) 35 40 ((message … … 41 46 (:documentation "thrown if the same identifier is already in use")) 42 47 48 43 49 (define-condition object-not-found-error(error) 44 50 ((message … … 46 52 :accessor message)) 47 53 (:documentation "thrown if the object could not be found")) 54 48 55 49 56 (define-condition no-identifier-error(error) … … 55 62 :accessor internal-id)) 56 63 (:documentation "thrown if the topic has no identifier")) 64 65 66 (define-condition not-mergable-error (error) 67 ((message 68 :initarg :message 69 :accessor message) 70 (construc-1 71 :initarg :construct-1 72 :accessor construct-1) 73 (construc-2 74 :initarg :construct-2 75 :accessor construct-2)) 76 (:documentation "Thrown if two constructs are not mergable since 77 they have e.g. difference types.")) 78 79 80 (define-condition missing-argument-error (error) 81 ((message 82 :initarg :message 83 :accessor message) 84 (argument-symbol 85 :initarg :argument-symbol 86 :accessor argument-symbol) 87 (function-symbol 88 :initarg :function-symbol 89 :accessor function-symbol)) 90 (:documentation "Thrown if a argument is missing in a function.")) 91 92 93 (define-condition tm-reference-error (error) 94 ((message 95 :initarg :message 96 :accessor message) 97 (referenced-construct 98 :initarg :referenced-construct 99 :accessor referenced-construct) 100 (existing-reference 101 :initarg :existing-reference 102 :accessor existing-reference) 103 (new-reference 104 :initarg :new-reference 105 :accessor new-reference)) 106 (:documentation "Thrown of the referenced-construct is already owned by another 107 TM-construct (existing-reference) and is going to be referenced 108 by a second TM-construct (new-reference) at the same time."))
Note: See TracChangeset
for help on using the changeset viewer.