Changeset 325 for trunk/src/model


Ignore:
Timestamp:
10/10/10 09:41:19 (15 years ago)
Author:
lgiessmann
Message:

merged the branch "new-datamodel" with "trunk" -> resolved all conflicts, except -> the remove-handler of the ui isn't supported by the backend yet

Location:
trunk/src/model
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified trunk/src/model/changes.lisp

    r229 r325  
    88
    99
    10 ;-*- standard-indent:2; tab-width:2; indent-tabs-mode:nil -*-
    1110(in-package :datamodel)
    1211
    1312(defun get-all-revisions ()
    1413  "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))
    2119    (dolist (vi (elephant:get-instances-by-class 'VersionInfoC))
    2220      (pushnew (start-revision vi) revision-set))
    2321    (sort revision-set #'<)))
     22
    2423
    2524(defun get-all-revisions-for-tm (tm-id)
     
    3029       (tops-and-assocs (when tm (union (topics tm) (associations tm))))
    3130       (revision-set nil))
    32     ;(format t "tops-and-assocs: ~a~&" (mapcan #'versions tops-and-assocs))
    3331    (dolist (vi (mapcan #'versions tops-and-assocs))
    34       ;(format t "(start-revision vi): ~a~&" (start-revision vi))
    3532      (pushnew (start-revision vi) revision-set))
    3633    (sort revision-set #'<)))
    3734
    3835
    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)))))
    5260 
    5361
    54 (defgeneric find-referenced-topics (construct)
     62(defgeneric find-referenced-topics (construct &key revision)
    5563  (:documentation "find all the topics that are references from this construct as type, scope or player, as the case may be"))
    5664
    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"
    5969  (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))))
    6580   (when  (and (typep characteristic 'OccurrenceC)
    6681              (> (length (charvalue characteristic)) 0)
    6782              (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*))
    7289  (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*))
    7998  "associations are scopable + typable"
    8099  (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))))
    86107 
    87108
    88 (defmethod find-referenced-topics ((top TopicC))
     109(defmethod find-referenced-topics ((top TopicC)
     110                                   &key (revision *TM-REVISION*))
    89111  "Part 1b of the eGov-Share spec states:
    90112# for each topicname in T export a topic stub for each scope topic
     
    99121    top
    100122    (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))))))
    106136   
    107137
     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
    108150(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:
    110153    * newly created
     154    * deletion of an element
    111155    * 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
    115163
    116164(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))))
    147303
    148304
     
    192348        (remove
    193349         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))))))
    203364
    204365(defun get-fragment (unique-id)
     
    209370                                  unique-id))
    210371
    211 (defgeneric mark-as-deleted (construct &key source-locator revision)
    212   (:documentation "Mark a construct as deleted if it comes from the source indicated by
    213 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 by
    217 source-locator"
    218   (declare (ignorable source-locator))
    219   (let
    220       ((last-version ;the last active version
    221         (find 0 (versions construct) :key #'end-revision)))
    222     (when last-version
    223       (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 by
    233 source-locator"
    234   ;;Part 1b, 1.4.3.3.1:
    235   ;; Let SP be the value of the ServerSourceLocatorPrefix element in the ATOM feed F
    236   ;; * Let SI be the value of TopicSI element in ATOM entry E
    237   ;; * feed F contains E
    238   ;; * entry E references topic fragment TF
    239   ;; * Let LTM be the local topic map
    240   ;; * Let T be the topic in LTM that has a subjectidentifier that matches SI
    241   ;; * For all names, occurrences and associations in which T plays a role, TMC
    242   ;;   * Delete all SrcLocators of TMC that begin with SP. If the count of srclocators on TMC = 0 then delete TMC
    243   ;;   * Merge in the fragment TF using SP as the base all generated source locators.
    244 
    245   (when
    246       (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 
    255372(defgeneric add-source-locator (construct &key source-locator revision)
    256373  (: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
    258376
    259377(defmethod add-source-locator ((construct ReifiableConstructC) &key source-locator revision)
    260   (declare (ignorable revision))
     378  (declare (integer revision))
    261379  (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))
    263383    (let
    264384        ((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
    266390
    267391(defmethod add-source-locator ((top TopicC) &key source-locator revision)
     
    269393  ;do not need to add an extra item identifier to them. However, we
    270394  ;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)))
    277407
    278408
     
    280410  "Returns the latest fragment of the passed topic-psi"
    281411  (declare (string topic-psi))
    282   (let ((topic
    283          (get-item-by-psi topic-psi)))
     412  (let ((topic (get-latest-topic-by-psi topic-psi)))
    284413    (when topic
    285414      (let ((start-revision
     
    298427              (make-instance 'FragmentC
    299428                             :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)
    302433                             :topic topic)))))))
    303434
     
    306437  "Returns the latest existing fragment of the passed topic-psi."
    307438  (declare (string topic-psi))
    308   (let ((topic
    309          (get-item-by-psi topic-psi)))
     439  (let ((topic (get-latest-topic-by-psi topic-psi)))
    310440    (when topic
    311441      (let ((existing-fragments
  • TabularUnified trunk/src/model/datamodel.lisp

    r284 r325  
    88
    99
    10 ;-*- standard-indent: 2; indent-tabs-mode: nil -*-
    1110(defpackage :datamodel
    1211  (:use :cl :elephant :constants)
    1312  (:nicknames :d)
    1413  (: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
    2632           :OccurrenceC
     33           :NameC
     34           :VariantC
     35           :PointerC
     36           :IdentifierC
    2737           :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
    9995           :used-as-type
    10096           :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
    104112           :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
    119160(in-package :datamodel)
    120161
    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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    164169(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
    165756
    166757(defmacro with-revision (revision &rest body)
    167758  `(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
    208818
    209819(defun get-revision ()
     
    211821  (get-universal-time))
    212822
    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)
    444957  (:documentation "Check for possibly duplicate identifiers and signal an
    445958  duplicate-identifier-error is such duplicates are found"))
    446959
    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))
    5141076  (let ((result (find 0 (versions construct) :key #'end-revision)))
    5151077    (if result
     
    5211083            (first sorted-list)))))) ;latest version-info of marked-as-deleted constructs -> highest integer
    5221084
    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))))
    7571342       
    7581343
    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
    7762337
    7772338(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 
    8682529
    8692530(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))
    11602630  (call-next-method))
    11612631
    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)))
    14392760
    14402761
     
    14432764
    14442765
    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*))
    15873543  (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*))
    15923549  (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  
    1414           :no-identifier-error
    1515           :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))
    1720
    1821(in-package :exceptions)
     
    2225    :initarg :message
    2326    :accessor message)))
     27
    2428
    2529(define-condition missing-reference-error(error)
     
    3236  (:documentation "thrown is a reference is missing"))
    3337
     38
    3439(define-condition duplicate-identifier-error(error)
    3540  ((message
     
    4146  (:documentation "thrown if the same identifier is already in use"))
    4247
     48
    4349(define-condition object-not-found-error(error)
    4450  ((message
     
    4652    :accessor message))
    4753  (:documentation "thrown if the object could not be found"))
     54
    4855
    4956(define-condition no-identifier-error(error)
     
    5562    :accessor internal-id))
    5663  (: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.