Author: lgiessmann Date: Thu Feb 11 14:21:40 2010 New Revision: 183
Log: new-datamodel: started to implement the new datamodel --> added some base classes
Added: branches/new-datamodel/src/unit_tests/datamodel_test.lisp Modified: branches/new-datamodel/src/isidorus.asd branches/new-datamodel/src/model/datamodel.lisp branches/new-datamodel/src/unit_tests/fixtures.lisp
Modified: branches/new-datamodel/src/isidorus.asd ============================================================================== --- branches/new-datamodel/src/isidorus.asd (original) +++ branches/new-datamodel/src/isidorus.asd Thu Feb 11 14:21:40 2010 @@ -147,6 +147,8 @@ :depends-on ("fixtures")) (:file "rdf_exporter_test" :depends-on ("fixtures")) + (:file "datamodel_test" + :depends-on ("fixtures")) (:file "reification_test" :depends-on ("fixtures" "unittests-constants"))) :depends-on ("atom"
Modified: branches/new-datamodel/src/model/datamodel.lisp ============================================================================== --- branches/new-datamodel/src/model/datamodel.lisp (original) +++ branches/new-datamodel/src/model/datamodel.lisp Thu Feb 11 14:21:40 2010 @@ -7,1670 +7,1962 @@ ;;+-----------------------------------------------------------------------------
-;-*- standard-indent: 2; indent-tabs-mode: nil -*- (defpackage :datamodel (:use :cl :elephant :constants) - (:nicknames :d) - (:import-from :exceptions - missing-reference-error - no-identifier-error - duplicate-identifier-error - object-not-found-error) - (:export :AssociationC ;; types - :CharacteristicC - :FragmentC - :IdentifierC - :IdentityC - :ItemIdentifierC - :NameC - :OccurrenceC - :PersistentIdC - :ReifiableConstructC - :RoleC - :ScopableC - :SubjectLocatorC - :TopicC - :TopicIdentificationC - :TopicMapC - :TopicMapConstructC - :TypableC - :VariantC - - ;; functions and slot accessors - :in-topicmaps - :add-to-topicmap - :add-source-locator - :associations - :changed-p - :charvalue - :check-for-duplicate-identifiers - :datatype - :equivalent-constructs - :find-item-by-revision - :find-most-recent-revision - :get-all-revisions - :get-all-revisions-for-tm - :get-fragment - :get-fragments - :get-revision - :get-item-by-content - :get-item-by-id - :get-item-by-item-identifier - :get-item-by-psi - :identified-construct - :identified-construct-p - :in-topicmap - :internal-id - :instance-of - :instance-of-p - :item-identifiers - :item-identifiers-p - :list-instanceOf - :list-super-types - :locators - :locators-p - :make-construct - :mark-as-deleted - :names - :namevalue - :occurrences - :name - :parent - :player - :player-in-roles - :players - :psis - :psis-p - :referenced-topics - :revision - :RoleC-p - :roleid - :roles - :themes - :xtm-id - :xtm-id-p - :topic - :topicid - :topic-identifiers - :topics - :unique-id - :uri - :uri-p - :used-as-type - :used-as-theme - :variants - :xor - :create-latest-fragment-of-topic - :reified - :reifier - :add-reifier - :remove-reifier - - :*current-xtm* ;; special variables - :*TM-REVISION* + (:nicknames :d))
- :with-revision ;;macros +(in-package :datamodel)
- :string-starts-with ;;helpers - ))
-(declaim (optimize (debug 3) (safety 3) (speed 0) (space 0))) -(in-package :datamodel) +;;; some helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun slot-p (instance slot-symbol) + "Returns t if the slot depending on slot-symbol is bound and not nil." + (and (slot-boundp instance slot-symbol) + (slot-value instance slot-symbol))) +
-(defparameter *current-xtm* nil "Represents the currently active TM") +(defun delete-1-n-association(instance slot-symbol) + (when (slot-p instance slot-symbol) + (remove-association + instance slot-symbol (slot-value instance slot-symbol))))
-(defmacro find-max-elem (candidate-list &key (relop #'> relop-p) (key #'identity key-p)) - "Given a non-empty list, return the maximum element in the list. - If provided, then relop must be a relational operator that determines the ordering; - else #'> is used. The keyword parameter key may name a function that is used to extract - the sort key; otherwise the elements themselves are the sort keys." - (let - ((candidate-list-value-name (gensym)) - (relop-value-name (gensym)) - (key-value-name (gensym)) - (best-seen-cand-name (gensym)) - (max-key-name (gensym)) - (inspected-cand-name (gensym)) - (inspected-key-name (gensym))) - (let - ((max-key-init (if key-p - `(funcall ,key-value-name ,best-seen-cand-name) - best-seen-cand-name)) - (inspected-key-init (if key-p - `(funcall ,key-value-name ,inspected-cand-name) - inspected-cand-name)) - (relexp (if relop-p - `(funcall ,relop-value-name ,inspected-key-name ,max-key-name) - `(> ,inspected-key-name ,max-key-name)))) - (let - ((initializers `((,candidate-list-value-name ,candidate-list) - (,best-seen-cand-name (first ,candidate-list-value-name)) - (,max-key-name ,max-key-init)))) - (when relop-p - (push `(,relop-value-name ,relop) initializers)) - (when key-p - (push `(,key-value-name ,key) initializers)) - `(let* - ,initializers - (dolist (,inspected-cand-name (rest ,candidate-list-value-name)) - (let - ((,inspected-key-name ,inspected-key-init)) - (when ,relexp - (setf ,best-seen-cand-name ,inspected-cand-name) - (setf ,max-key-name ,inspected-key-name)))) - ,best-seen-cand-name))))) - -(defvar *TM-REVISION* 0) - -(defmacro with-revision (revision &rest body) - `(let - ((*TM-REVISION* ,revision)) - ;(format t "*TM-REVISION* is ~a~&" *TM-REVISION*) - ,@body)) - - -(defmacro slot-predicate (instance slot) - (let - ((inst-name (gensym)) - (slot-name (gensym))) - `(let - ((,inst-name ,instance) - (,slot-name ,slot)) - (and (slot-boundp ,inst-name ,slot-name) - (slot-value ,inst-name ,slot-name))))) - -(defmacro delete-1-n-association (instance slot) - (let - ((inst-name (gensym)) - (slot-name (gensym))) - `(let - ((,inst-name ,instance) - (,slot-name ,slot)) - (when (slot-predicate ,inst-name ,slot-name) - (elephant:remove-association ,inst-name ,slot-name (slot-value ,inst-name ,slot-name)))))) - -(defun xor (a1 a2) - (and (or a1 a2) (not (and a1 a2))) - ) - -(defun remove-nil-values (plist) - (let - ((result nil)) - (do* ((rest plist (cddr rest)) - (key (first rest) (first rest)) - (val (second rest) (second rest))) - ((null rest)) - (when val - (pushnew val result) - (pushnew key result))) - result)) - -(defun get-revision () - "TODO: replace by something that does not suffer from a 1 second resolution." - (get-universal-time))
(defgeneric delete-construct (construct) - (:documentation "drops recursively construct and all its dependent objects from the elephant store")) + (:documentation "Drops recursively construct and all its dependent objects + from the elephant store.")) +
(defmethod delete-construct ((construct elephant:persistent)) nil)
+ (defmethod delete-construct :after ((construct elephant:persistent)) - (elephant:drop-instance construct)) + (drop-instance construct))
-(defgeneric find-all-equivalent (construct) - (:method ((construct t)) nil) - (:documentation "searches an existing object that is equivalent (but not identical) to construct"))
+;;; VersionInfoC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defpclass VersionInfoC() + ((start-revision :initarg :start-revision + :accessor start-revision + :type integer + :initform 0 + :documentation "The start-revision of the version's + interval of a versioned object.") + (end-revision :initarg :end-revision + :accessor end-revision + :type integer + :initform 0 + :documentation "The end-revision of the version's interval + of a versioned object.") + (versioned-construct :initarg :versioned-construct + :accessor versioned-construct + :associate VersionedConstructC + :documentation "The reference of the versioned + object that is described by this + VersionInfoC-object.")) + (:documentation "A VersionInfoC-object describes the revision information + of a versioned object in intervals starting by the value + start-revision and ending by the value end-revision - 1. + end-revision=0 means always the latest version.")) + + +(defmethod delete-construct :before ((version-info VersionInfoC)) + (delete-1-n-association version-info 'versioned-construct)) + + +(defgeneric versioned-construct-p (version-info) + (:documentation "Returns t if the passed object is already bound to a + VersionedObjectC.") + (:method ((version-info VersionInfoC)) + (slot-p version-info 'versioned-construct))) + + +;;; VersionedConstructC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defpclass VersionedConstructC() + ((versions :initarg :versions + :accessor versions + :associate (VersionInfoC versioned-construct) + :documentation "Version infos for former versions of this base + class."))) + + +(defmethod delete-construct :before ((construct VersionedConstructC)) + (dolist (version-info (versions construct)) + (delete-construct version-info))) + + +(defgeneric get-most-recent-version-info (construct) + (:documentation "Returns the latest VersionInfoC object of the passed + versioned construct. + The latest construct is either the one with + end-revision=0 or with the highest end-revision value."))
-;;;;;;;;;;;;;; -;; -;; VersionInfoC
+(defmethod get-most-recent-version-info ((construct VersionedConstructC)) + (let ((result (find 0 (versions construct) :key #'end-revision))) + (if result + result ;current version-info -> end-revision = 0 + (let ((sorted-list (sort (versions construct) + #'(lambda(x y) + (> (end-revision x) (end-revision y)))))) + (when sorted-list + (first sorted-list)))))) ;latest version-info of marked-as-deleted constructs -> highest integer
-(elephant:defpclass VersionInfoC () - ((start-revision :accessor start-revision - :initarg :start-revision - :type integer - :initform 0 ;TODO: for now - :documentation "The first revison this AssociationC instance is associated with.") - (end-revision :accessor end-revision - :initarg :end-revision - :type integer - :initform 0 ;TODO: for now - :documentation "The first revison this AssociationC instance is no longer associated with.") - (versioned-construct :associate TopicMapConstructC - :accessor versioned-construct - :initarg :versioned-construct - :documentation "reifiable construct that is described by this info")) - (:documentation "Version Info for individual revisions"))
-(defgeneric versioned-construct-p (vi) - (:documentation "t if this version info is already bound to a TM construct") - (:method ((vi VersionInfoC)) (slot-predicate vi 'versioned-construct))) +(defgeneric add-to-version-history (construct &key start-revision end-revision) + (:documentation "Adds version history to a versioned construct"))
-(defmethod delete-construct :before ((vi VersionInfoC)) - (delete-1-n-association vi 'versioned-construct))
-(defgeneric get-most-recent-version-info (construct)) +(defmethod add-to-version-history ((construct VersionedConstructC) + &key (start-revision (error "From add-to-version-history(): start revision must be present")) + (end-revision 0)) + (let ((eql-version-info + (find-if #'(lambda(vi) + (and (= (start-revision vi) start-revision) + (= (end-revision vi) end-revision))) + (versions construct)))) + (if eql-version-info + eql-version-info + (let ((current-version-info + (get-most-recent-version-info construct))) + (cond + ((and current-version-info + (= (end-revision current-version-info) start-revision)) + (setf (end-revision current-version-info) 0) + current-version-info) + ((and current-version-info + (= (end-revision current-version-info) 0)) + (setf (end-revision current-version-info) start-revision) + (make-instance 'VersionInfoC + :start-revision start-revision + :end-revision end-revision + :versioned-construct construct)) + (t + (make-instance 'VersionInfoC + :start-revision start-revision + :end-revision end-revision + :versioned-construct construct))))))) + + +;;; Versioned-Associations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; SubjectLocatorAssociationC +;;; PersistentIdAssociationC +;;; TopicIdAssociationC +;;; ItemIdAssociationC +;;; PointerAssociationC +;;; VersionedAssociationC +(defpclass SubjectLocatorAssociationC(PointerAssociationC) + ((identified-construct :initarg :identified-construct + :accessor identified-construct + :associate TopicC + :documentation "The actual topic which is associated + with the subject-locator.")) + (:index t) + (:documentation "A pointer that associates subject-locators, versions + and topics."))
-;;;;;;;;;;;;;; -;; -;; ItemIdentifierC +(defpclass PersistentIdAssociationC(PointerAssociationC) + ((identified-construct :initarg :identified-construct + :accessor identified-construct + :associate TopicC + :documentation "The actual topic which is associated + with the subject-identifier/psi.")) + (:index t) + (:documentation "A pointer that associates subject-identifiers, versions + and topics.")) +
-(elephant:defpclass ItemIdentifierC (IdentifierC) +(defpclass TopicIdAssociationC(PointerAssociationC) + ((identified-construct :initarg :identified-construct + :accessor identified-construct + :associate TopicC + :documentation "The actual topic which is associated + with the topic-identifier.")) + (:index t) + (:documentation "A pointer that associates topic-identifiers, versions + and topics.")) + + +(defpclass ItemIdAssociationC(PointerAssociationC) + ((identified-construct :initarg :identified-construct + :accessor identified-construct + :associate ReifiableConstructC + :documentation "The actual parent which is associated + with the item-identifier.")) + (:index t) + (:documentation "A pointer that associates item-identifiers, versions + and reifiable-constructs.")) + + +(defpclass PointerAssociationC (VersionedAssociationC) + ((identifier :initarg :identifier + :accessor identifier + :associate PointerC + :documentation "The actual data that is associated with + the pointer-association's parent.")) + (:documentation "An abstract base class for all versioned + pointer-associations.")) + + +(defpclass VersionedAssociationC() + () + (:documentation "An abstract base class for all versioned associations.")) + + +;;; Pointers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; SubjectLocatorC +;;; PersistentIdC +;;; ItemIdentifierC +;;; IdentifierC +;;; TopicIdentificationC +;;; PointerC +(defpclass SubjectLocatorC(IdentifierC) () (:index t) - (:documentation "Represents an item identifier")) + (:documentation "A subject-locator that contains an uri-value and an + association to SubjectLocatorAssociationC's which are in + turn associated with TopicC's."))
-;;;;;;;;;;;;;; -;; -;; SubjectLocator +(defpclass PersistentIdC(IdentifierC) + () + (:index t) + (:documentation "A subject-identifier that contains an uri-value and an + association to PersistentIdAssociationC's which are in + turn associated with TopicC's."))
-(elephant:defpclass SubjectLocatorC (IdentifierC) - ((identified-construct :accessor identified-construct - :initarg :identified-construct - :associate TopicC)) + +(defpclass ItemIdentifierC(IdentifierC) + () (:index t) - (:documentation "Represents a subject locator")) + (:documentation "An item-identifier that contains an uri-value and an + association to ItemIdAssociationC's which are in turn + associated with RiefiableConstructC's."))
-;;;;;;;;;;;;;; -;; -;; IdentifierC +(defpclass IdentifierC(PointerC) + () + (:documentation "An abstract base class for all TM-Identifiers."))
-(elephant:defpclass IdentifierC (PointerC) + +(defpclass TopicIdentificationC(PointerC) + ((xtm-id :initarg :xtm-id + :accessor xtm-id + :type string + :initform (error "From TopicIdentificationC(): xtm-id must be seet for a topic-identifier") + :index t + :documentation "ID of the TM this identification came from.")) + (:index t) + (:documentation "Identify topic items through generalized topic-ids. + A topic may have many original topicids, the class + representing one of them.")) + + +(defpclass PointerC(TopicMapConstructC) + ((uri :initarg :uri + :accessor uri + :type string + :initform (error "From PointerC(): uri must be set for a pointer") + :index t + :documentation "The actual value of a pointer, i.e. uri or ID.") + (identified-construct :initarg :identified-construct + :accessor identified-construct + :associate (PointerAssociationC identifier))) + (:documentation "An abstract base class for all pointers.")) + + +;;; ReifiableConstructC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defpclass ReifiableConstructC(TopicMapConstructC) + ((item-identifiers :initarg :item-identifiers + :associate (ItemIdAssociationC identified-construct) + :documentation "A relation to all item-identifiers of + this construct.") + (reifier :initarg :reifier + :associate (ReifierAssociationC reified-construct) + :documentation "A relation to a reifier-topic.")) + (:documentation "Reifiable constructs as per TMDM.")) + + +;;TODO: implement reader for item-identifiers and reifier (version) +;;TODO: implement add-item-identifier and add-reifier (version) + + +;;; TopicMapConstructC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defpclass TopicMapConstructC() () - (:documentation "Abstract base class for ItemIdentifierC and - PersistentIdC, primarily in view of the equality rules")) + (:documentation "An abstract base class for all classes that describes + Topic Maps data."))
-;;;;;;;;;;;;;; -;; -;; PointerC
-(elephant:defpclass PointerC (TopicMapConstructC) - ((uri :accessor uri - :initarg :uri - :type string - :initform (error "The uri must be set for a pointer") - :index t) - (identified-construct :accessor identified-construct - :initarg :identified-construct - :associate ReifiableConstructC)) - (:documentation "Abstract base class for all types of pointers and identifiers")) - -(defmethod delete-construct :before ((construct PointerC)) - (delete-1-n-association construct 'identified-construct)) - -(defmethod find-all-equivalent ((construct PointerC)) - (delete construct - (elephant:get-instances-by-value (class-of construct) - 'uri - (uri construct)) - :key #'internal-id)) -(defgeneric uri-p (construct) - (:documentation "Check if the slot uri is bound in an identifier and not nil") - (:method ((identifier PointerC)) (slot-predicate identifier 'uri))) - -(defgeneric identified-construct-p (construct) - (:documentation "Check if the slot identified-construct is bound in an identifier and not nil") - (:method ((identifier PointerC)) (slot-predicate identifier 'identified-construct))) - -(defmethod print-object ((identifier PointerC) stream) - (format stream - "~a(href: ~a; Construct: ~a)" - (class-name (class-of identifier)) - (if (uri-p identifier) - (uri identifier) - "URI UNDEFINED") - (if (identified-construct-p identifier) - (identified-construct identifier) - "SLOT UNBOUND"))) - -(defmethod equivalent-constructs ((identifier1 PointerC) (identifier2 PointerC)) - (string= (uri identifier1) (uri identifier2))) - -(defmethod initialize-instance :around ((identifier PointerC) &key - (start-revision (error "Start revision must be present") ) - (end-revision 0)) - (call-next-method) - (add-to-version-history identifier - :start-revision start-revision - :end-revision end-revision) - identifier) - - -;;;;;;;;;;;;;; -;; -;; TopicMapConstrucC - - -(elephant:defpclass TopicMapConstructC () - ((versions :associate (VersionInfoC versioned-construct) - :accessor versions - :initarg :versions - :documentation "version infos for former versions of this reifiable construct"))) - - ;TODO: if, one day, we allow merges of already existing constructs, we'll need - ;a tree of predecessors rather then just a list of versions. A case in point - ;may be if a newly imported topic carries the PSIs of two existing topics, - ;thereby forcing a merge post factum" - -(defmethod delete-construct :before ((construct TopicMapConstructC)) - (dolist (versioninfo (versions construct)) - (delete-construct versioninfo)))
-(defgeneric add-to-version-history (construct &key start-revision end-revision) - (:documentation "Add version history to a topic map construct"))
-(defmethod add-to-version-history ((construct TopicMapConstructC) - &key - (start-revision (error "Start revision must be present") ) - (end-revision 0)) - "Adds relevant information to a construct's version info" - (let - ((current-version-info - (get-most-recent-version-info construct))) - (cond - ((and current-version-info - (= (end-revision current-version-info) start-revision)) ;the item was just marked as deleted - (setf (end-revision current-version-info) 0) ;just revitalize it, do not create a new version - current-version-info) ;TODO: this is not quite correct, the topic - ;might be recreated with new item - ;identifiers. Consider adding a new parameter - ;"revitalize" - ((and - current-version-info - (= (end-revision current-version-info) 0)) - (setf (end-revision current-version-info) start-revision) - (make-instance - 'VersionInfoC - :start-revision start-revision - :end-revision end-revision - :versioned-construct construct)) - (t - (make-instance - 'VersionInfoC - :start-revision start-revision - :end-revision end-revision - :versioned-construct construct))))) - -(defgeneric revision (constr) - (:documentation "Essentially a convenience method for start-revision")) - -(defmethod revision ((constr TopicMapConstructC)) - (start-revision constr)) - -(defmethod (setf revision) ((constr TopicMapConstructC) (revision integer)) - (setf (start-revision constr) revision)) - - -(defgeneric find-item-by-revision (constr revision) - (:documentation "Get a given version of a construct (if any, nil if none can be found)")) - -(defmethod find-item-by-revision ((constr TopicMapConstructC) (revision integer)) - (cond - ((= revision 0) - (find-most-recent-revision constr)) - (t - (when (find-if - (lambda(version) - (and (>= revision (start-revision version)) - (or - (< revision (end-revision version)) - (= 0 (end-revision version))))) - (versions constr)) - constr)))) - -(defgeneric find-most-recent-revision (construct) - (:documentation "Get the most recent version of a construct (nil if -the construct doesn't have versions yet or not anymore)")) - -(defmethod find-most-recent-revision ((construct TopicMapConstructC)) - (when (find 0 (versions construct) :key #'end-revision) - construct)) - -(defmethod delete-construct :before ((construct TopicMapConstructC)) - (dolist (versionInfo (versions construct)) - (delete-construct versionInfo))) - - -(defgeneric check-for-duplicate-identifiers (top) - (:documentation "Check for possibly duplicate identifiers and signal an - duplicate-identifier-error is such duplicates are found")) - -(defmethod check-for-duplicate-identifiers ((construct TopicMapConstructC)) - (declare (ignore construct)) - ;do nothing - ) - -(defgeneric filter-slot-value-by-revision (construct slot-name &key start-revision) - (:documentation "filter slot values by a given revision that is - either provided directly through the keyword argument start-revision - or through a bound variable named '*TM-REVISION*'")) - -(defmethod filter-slot-value-by-revision ((construct TopicMapConstructC) (slot-name symbol) &key (start-revision 0 start-revision-provided-p)) - (let - ((revision ;avoids warnings about undefined variables - (cond - (start-revision-provided-p - start-revision) - ((boundp '*TM-REVISION*) - (symbol-value '*TM-REVISION*)) - (t 0))) - (properties (slot-value construct slot-name))) - ;(format t "revision in filter-slot-value-by-revision is ~a~&" revision) - (cond - ((not properties) - nil) ;if we don't have any properties, we don't have to worry - ;about revisions - ((= 0 revision) - (remove - nil - (map 'list #'find-most-recent-revision - properties))) - (t - (remove nil - (map 'list - (lambda (constr) - (find-item-by-revision constr revision)) - properties)))))) - -(defgeneric make-construct (classsymbol &key start-revision &allow-other-keys) - (:documentation "create a new topic map construct if necessary or -retrieve an equivalent one if available and update the revision -history accordingly. Return the object in question. Methods use -specific keyword arguments for their purpose")) - -(defmethod make-construct ((classsymbol symbol) &rest args - &key start-revision) - (let* - ((cleaned-args (remove-nil-values args)) - (new-construct (apply #'make-instance classsymbol cleaned-args)) - (existing-construct (first (find-all-equivalent new-construct)))) - (if existing-construct - (progn - ;change over new item identifiers to the old construct - (when (copy-item-identifiers - new-construct existing-construct) - ;an existing construct other than a topic (which is handled - ;separatedly below) has changed only if it has received a new - ;item identifier - (add-to-version-history existing-construct :start-revision start-revision)) - (delete-construct new-construct) - existing-construct) - (progn - (add-to-version-history new-construct :start-revision start-revision) - (check-for-duplicate-identifiers new-construct) - new-construct)))) - -(defmethod get-most-recent-version-info ((construct TopicMapConstructC)) - (let ((result (find 0 (versions construct) :key #'end-revision))) - (if result - result ;current version-info -> end-revision = 0 - (let ((sorted-list (sort (versions construct) - #'(lambda(x y) - (> (end-revision x) (end-revision y)))))) - (when sorted-list - (first sorted-list)))))) ;latest version-info of marked-as-deleted constructs -> highest integer
-(defgeneric equivalent-constructs (construct1 construct2) - (:documentation "checks if two topic map constructs are equal according to the TMDM equality rules"))
-(defgeneric strictly-equivalent-constructs (construct1 construct2) - (:documentation "checks if two topic map constructs are not identical but equal according to the TMDM equality rules") - (:method ((construct1 TopicMapConstructC) (construct2 TopicMapConstructC)) - (and (equivalent-constructs construct1 construct2) - (not (eq construct1 construct2))))) - -(defgeneric internal-id (construct) - (:documentation "returns the internal id that uniquely identifies a - construct (currently simply its OID)")) - -(defmethod internal-id ((construct TopicMapConstructC)) - (slot-value construct (find-symbol "OID" 'elephant))) - - -;;;;;;;;;;;;;; -;; -;; TopicIdentificationC - -(elephant:defpclass TopicIdentificationC (PointerC) - ((xtm-id - :accessor xtm-id - :type string - :initarg :xtm-id - :index t - :documentation "ID of the TM this identification came from")) - (:documentation "Identify topic items through generalized - topicids. A topic may have many original topicids, the class - representing one of them") ) - -(defmethod find-all-equivalent ((construct TopicIdentificationC)) - (delete (xtm-id construct) (call-next-method) :key #'xtm-id :test #'string=)) - -(defun init-topic-identification (top id xtm-id &key (revision *TM-REVISION*)) - "create a TopicIdentification object (if necessary) and initialize it with the - combination of the current topicid and the ID of the current XTM id" - ;(declare (TopicC top)) - (declare (string id)) - - (flet ;prevent unnecessary copies of TopicIdentificationC objects - ((has-topic-identifier (top uri xtm-id) - (remove-if-not - (lambda (ident) - (and (string= (uri ident) uri) - (string= (xtm-id ident) xtm-id))) - (topic-identifiers top)))) - (unless (has-topic-identifier top id xtm-id) - (let - ((ti - (make-instance - 'TopicIdentificationC - :uri id - :xtm-id xtm-id - :identified-construct top - :start-revision revision))) - ;(add-to-version-history ti :start-revision revision) - ti)))) - -(defun xtm-id-p (xtm-id) - "checks if a xtm-id has been used before" - (elephant:get-instance-by-value 'TopicIdentificationC - 'xtm-id xtm-id)) - - -;;;;;;;;;;;;;; -;; -;; PSI - -(elephant:defpclass PersistentIdC (IdentifierC) - ((identified-construct :accessor identified-construct - :initarg :identified-construct - :associate TopicC)) - (:index t) - (:documentation "Represents a PSI"))
-;;;;;;;;;;;;;; +;; (:import-from :exceptions +;; missing-reference-error +;; no-identifier-error +;; duplicate-identifier-error +;; object-not-found-error) +;; (:export :AssociationC ;; types +;; :CharacteristicC +;; :FragmentC +;; :IdentifierC +;; :IdentityC +;; :ItemIdentifierC +;; :NameC +;; :OccurrenceC +;; :PersistentIdC +;; :ReifiableConstructC +;; :RoleC +;; :ScopableC +;; :SubjectLocatorC +;; :TopicC +;; :TopicIdentificationC +;; :TopicMapC +;; :TopicMapConstructC +;; :TypableC +;; :VariantC ;; -;; ReifiableConstructC - -(elephant:defpclass ReifiableConstructC (TopicMapConstructC) - ((item-identifiers - :associate (ItemIdentifierC identified-construct) - :inherit t - :documentation "Slot that realizes a 1 to N - relation between reifiable constructs and their - identifiers; pseudo-initarg is :item-identifiers. Is inherited by all reifiable constructs") - (reifier - :associate TopicC - :inherit t - :documentation "Represents a reifier association to a topic, i.e. - it stands for a 1:1 association between this class and TopicC")) - (:documentation "Reifiable constructs as per TMDM")) - - -(defgeneric reifier (construct &key revision) - (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) - (when (slot-boundp construct 'reifier) - (slot-value construct 'reifier)))) - -(defgeneric (setf reifier) (topic TopicC) - (:method (topic (construct ReifiableConstructC)) - (setf (slot-value construct 'reifier) topic))) -; (setf (reified topic) construct))) - -(defgeneric item-identifiers (construct &key revision) - (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision construct 'item-identifiers :start-revision revision))) - -(defmethod initialize-instance :around ((instance ReifiableConstructC) &key (item-identifiers nil) (reifier nil)) - "adds associations to these ids after the instance was initialized." - (declare (list item-identifiers)) - (call-next-method) - (dolist (id item-identifiers) - (declare (ItemIdentifierC id)) - (setf (identified-construct id) instance)) - (when reifier - (add-reifier instance reifier)) - ;(setf (reifier instance) reifier)) - instance) - -(defmethod delete-construct :before ((construct ReifiableConstructC)) - (dolist (id (item-identifiers construct)) - (delete-construct id)) - (when (reifier construct) - (let ((reifier-topic (reifier construct))) - (remove-reifier construct) - (delete-construct reifier-topic)))) - -(defgeneric item-identifiers-p (constr) - (:documentation "Test for the existence of item identifiers") - (:method ((construct ReifiableConstructC)) (slot-predicate construct 'item-identifiers))) - -(defgeneric topicid (construct &optional xtm-id) - (:documentation "Return the ID of a construct")) - -(defmethod revision ((constr ReifiableConstructC)) - (start-revision constr)) - -(defgeneric (setf revision) (revision construct) - (:documentation "The corresponding setter method")) - -(defmethod (setf revision) ((revision integer) (constr ReifiableConstructC)) - (setf (start-revision constr) revision)) - -(defgeneric get-all-identifiers-of-construct (construct) - (:documentation "Get all identifiers that a given construct has")) - -(defmethod get-all-identifiers-of-construct ((construct ReifiableConstructC)) - (item-identifiers construct)) - -(defmethod check-for-duplicate-identifiers ((construct ReifiableConstructC)) - (dolist (id (get-all-identifiers-of-construct construct)) - (when (> (length - (union - (elephant:get-instances-by-value 'ItemIdentifierC 'uri (uri id)) - (union - (elephant:get-instances-by-value 'PersistentIdC 'uri (uri id)) - (elephant:get-instances-by-value 'SubjectLocatorC 'uri (uri id))))) - 1) - (error - (make-condition 'duplicate-identifier-error - :message (format nil "Duplicate Identifier ~a has been found" (uri id)) - :uri (uri id)))))) - -(defmethod copy-item-identifiers ((from-construct ReifiableConstructC) - (to-construct ReifiableConstructC)) - "Internal method to copy over item idenfiers from a construct to -another on. Returns the set of new identifiers" - (mapc - (lambda (identifier) - (setf (identified-construct identifier) - to-construct)) - (set-difference (item-identifiers from-construct) - (item-identifiers to-construct) - :key #'uri :test #'string=))) - -;;;;;;;;;;;;;; -;; -;; ScopableC - -(elephant:defpclass ScopableC () - ((themes :accessor themes - :associate (TopicC used-as-theme) - :inherit t - :many-to-many t - :documentation "list of this scope's themes; pseudo-initarg is :themes"))) - -(defmethod initialize-instance :around ((instance ScopableC) &key (themes nil)) - (declare (list themes)) - (call-next-method) - (dolist (theme themes) - (elephant:add-association instance 'themes theme)) - instance) - -(defmethod delete-construct :before ((construct ScopableC)) - (dolist (theme (themes construct)) - (elephant:remove-association construct 'themes theme))) - - -;;;;;;;;;;;;;; -;; -;; TypableC - -(elephant:defpclass TypableC () - ((instance-of :accessor instance-of - :initarg :instance-of - :associate TopicC - :inherit t - :documentation "topic that this construct is an instance of"))) - -(defmethod delete-construct :before ((construct TypableC)) - (when (instance-of-p construct) - (elephant:remove-association construct 'instance-of (instance-of construct)))) - -(defgeneric instance-of-p (construct) - (:documentation "is the instance-of slot bound and not nil") - (:method ((construct TypableC)) (slot-predicate construct 'instance-of))) - - -;; (defmethod equivalent-constructs ((scope1 ScopeC) (scope2 ScopeC)) -;; "scopes are equal if their themes are equal" -;; (let -;; ((themes1 -;; (map 'list #'internal-id (themes scope1))) -;; (themes2 -;; (map 'list #'internal-id (themes scope2)))) -;; (not (set-exclusive-or themes1 themes2 :key #'internal-id)))) - -;;;;;;;;;;;;;; -;; -;; CharacteristicC - - -(elephant:defpclass CharacteristicC (ReifiableConstructC ScopableC TypableC) - ((topic :accessor topic - :initarg :topic - :associate TopicC - :documentation "The topic that this characteristic belongs to") - (charvalue :accessor charvalue - :type string - :initarg :charvalue - :index t - :documentation "the value of the characteristic in the given scope")) - (:documentation "Scoped characteristic of a topic (meant to be used - as an abstract class)")) - -(defgeneric CharacteristicC-p (object) - (:documentation "test if object is a of type CharacteristicC") - (:method ((object t)) nil) - (:method ((object CharacteristicC)) object)) - -(defmethod delete-construct :before ((construct CharacteristicC)) - (delete-1-n-association construct 'topic)) - -(defun get-item-by-content (content &key (revision *TM-REVISION*)) - "Find characteristis by their (atomic) content" - (flet - ((get-existing-instances (classname) - (delete-if-not #'(lambda (constr) - (find-item-by-revision constr revision)) - (elephant:get-instances-by-value classname 'charvalue content)))) - (nconc (get-existing-instances 'OccurenceC) - (get-existing-instances 'NameC)))) - - - - -;;;;;;;;;;;;;; -;; -;; VariantC - -(elephant:defpclass VariantC (CharacteristicC) - ((datatype :accessor datatype - :initarg :datatype - :initform nil - :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)") - (name :accessor name - :initarg :name - :associate NameC - :documentation "references the NameC instance which is the owner of this element"))) - - -(defgeneric VariantC-p (object) - (:documentation "test if object is a of type VariantC") - (:method ((object t)) nil) - (:method ((object VariantC)) object)) - - -(defmethod delete-construct :before ((construct VariantC)) - (delete-1-n-association construct 'name)) - - -(defmethod find-all-equivalent ((construct VariantC)) - (let ((parent (and (slot-boundp construct 'name) - (name construct)))) - (when parent - (delete-if-not #'(lambda(x)(strictly-equivalent-constructs construct x)) - (slot-value parent 'variants))))) - - -(defmethod equivalent-constructs ((variant1 VariantC) (variant2 VariantC)) - "variant items are (TMDM(5.5)-)equal if the values of their - [value], [datatype], [scope], and [parent] properties are equal" - (and (string= (charvalue variant1) (charvalue variant2)) - (or (and (not (slot-boundp variant1 'datatype)) (not (slot-boundp variant2 'datatype))) - (and (slot-boundp variant1 'datatype) (slot-boundp variant2 'datatype) - (string= (datatype variant1) (datatype variant2)))) - (not (set-exclusive-or (themes variant1) (themes variant2) :key #'internal-id)))) - - - - -;;;;;;;;;;;;;; -;; -;; NameC - -(elephant:defpclass NameC (CharacteristicC) - ((variants ;:accessor variants - :associate (VariantC name))) - (:documentation "Scoped name of a topic")) - - -(defgeneric variants (name &key revision) - (:method ((name NameC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision name 'variants :start-revision revision))) - - -(defgeneric NameC-p (object) - (:documentation "test if object is a of type NameC") - (:method ((object t)) nil) - (:method ((object NameC)) object)) - - -(defmethod find-all-equivalent ((construct NameC)) - (let - ((parent (and (slot-boundp construct 'topic) - (topic construct)))) - (when parent - (delete-if-not - #'(lambda (cand) (strictly-equivalent-constructs construct cand)) - (slot-value parent 'names))))) - - -(defmethod delete-construct :before ((construct NameC)) - (dolist (variant (variants construct)) - (delete-construct variant))) - - -(defmethod equivalent-constructs ((name1 NameC) (name2 NameC)) - "check for the equlity of two names by the TMDM's equality -rules (5.4)" - (and - (string= (charvalue name1) (charvalue name2)) - (or (and (instance-of-p name1) - (instance-of-p name2) - (= (internal-id (instance-of name1)) - (internal-id (instance-of name2)))) - (and (not (instance-of-p name1)) (not (instance-of-p name2)))) - (not (set-exclusive-or (themes name1) (themes name2) :key #'internal-id)))) - - - - -;;;;;;;;;;;;;; -;; -;; OccurrenceC - -(elephant:defpclass OccurrenceC (CharacteristicC) - ((datatype :accessor datatype - :initarg :datatype - :initform nil - :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)"))) - - -(defgeneric OccurrenceC-p (object) - (:documentation "test if object is a of type OccurrenceC") - (:method ((object t)) nil) - (:method ((object OccurrenceC)) object)) - -(defmethod find-all-equivalent ((construct OccurrenceC)) - (let - ((parent (and (slot-boundp construct 'topic) - (topic construct)))) - (when parent - (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) - (slot-value parent 'occurrences))))) - -(defmethod equivalent-constructs ((occ1 OccurrenceC) (occ2 OccurrenceC)) - "Occurrence items are equal if the values of their [value], [datatype], [scope], [type], and [parent] properties are equal (TMDM 5.6)" - (and - (string= (charvalue occ1) (charvalue occ2)) - (not (set-exclusive-or (themes occ1) (themes occ2) :key #'internal-id)) - (= (internal-id (topic occ1)) (internal-id (topic occ2))) - (or - (and (instance-of-p occ1) (instance-of-p occ2) - (= - (internal-id (instance-of occ1)) - (internal-id (instance-of occ2)))) - (and (not (instance-of-p occ1)) (not (instance-of-p occ2)))))) - - -;;;;;;;;;;;;;;;;; -;; -;; TopicC - -(elephant:defpclass TopicC (ReifiableConstructC) - ((topic-identifiers - :accessor topic-identifiers - :associate (TopicIdentificationC identified-construct)) - (psis ;accessor written below - :associate (PersistentIdC identified-construct) - :documentation "list of PSI objects associated with this - topic") - (locators - ;accessor written below - :associate (SubjectLocatorC identified-construct) - :documentation "an optional URL that (if given) means that this topic is a subject locator") - (names ;accessor written below - :associate (NameC topic) - :documentation "list of topic names (as TopicC objects)") - (occurrences ;accessor occurrences explicitly written below - :associate (OccurrenceC topic) - :documentation "list of occurrences (as OccurrenceC objects)") - (player-in-roles ;accessor player-in-roles written below - :associate (RoleC player) - :documentation "the list of all role instances where this topic is a player in") - (used-as-type ;accessor used-as-type written below - :associate (TypableC instance-of) - :documentation "list of all constructs that have this topic as their type") - (used-as-theme ;accessor used-as-theme written below - :associate (ScopableC themes) - :many-to-many t - :documentation "list of all scopable objects this topic is a theme in") - (in-topicmaps - :associate (TopicMapC topics) - :many-to-many t - :documentation "list of all topic maps this topic is part of") - (reified - :associate ReifiableConstructC - :documentation "contains a reified object, represented as 1:1 association")) - (:documentation "Topic in a Topic Map")) - - -(defgeneric reified (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (when (slot-boundp topic 'reified) - (slot-value topic 'reified)))) - -(defgeneric (setf reified) (reifiable ReifiableConstructC) - (:method (reifiable (topic TopicC)) - (setf (slot-value topic 'reified) reifiable))) -; (setf (reifier reifiable) topic))) - -(defgeneric occurrences (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision topic 'occurrences :start-revision revision))) - -(defgeneric names (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision topic 'names :start-revision revision))) - -(defgeneric psis (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision - topic 'psis :start-revision revision))) - -(defgeneric locators (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision - topic 'locators :start-revision revision))) - -(defgeneric player-in-roles (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision - topic 'player-in-roles :start-revision revision))) - -(defgeneric used-as-type (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision topic 'used-as-type :start-revision revision))) - -(defgeneric used-as-theme (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision topic 'used-as-theme :start-revision revision))) - -(defgeneric in-topicmaps (topic &key revision) - (:method ((topic TopicC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision topic 'in-topicmaps :start-revision revision))) - -(defun move-identifiers(destination-topic source-topic &key (what 'item-identifiers)) - "Moves all identifiers from the source-topic to the destination topic." - (declare (TopicC destination-topic source-topic)) - (let ((all-source-identifiers - (cond - ((eql what 'item-identifiers) - (item-identifiers source-topic)) - ((eql what 'locators) - (locators source-topic)) - (t - (psis source-topic)))) - (all-destination-identifiers - (cond - ((eql what 'item-identifiers) - (item-identifiers destination-topic)) - ((eql what 'locators) - (locators destination-topic)) - ((eql what 'psis) - (psis destination-topic)) - ((eql what 'topic-identifiers) - (topic-identifiers destination-topic))))) - (let ((identifiers-to-move - (loop for id in all-source-identifiers - when (not (find-if #'(lambda(x) - (if (eql what 'topic-identifiers) - (string= (xtm-id x) (xtm-id id)) - (string= (uri x) (uri id)))) - all-destination-identifiers)) - collect id))) - (dolist (item identifiers-to-move) - (remove-association source-topic what item) - (add-association destination-topic what item))))) - -(defmethod initialize-instance :around ((instance TopicC) &key (psis nil) (locators nil) (reified nil)) - "implement the pseudo-initargs :topic-ids, :persistent-ids, and :subject-locators" - (declare (list psis)) - (declare (list locators)) - (call-next-method) - ;item-identifiers are handled in the around-method for ReifiableConstructs, - ;TopicIdentificationCs are handled in make-construct of TopicC - (dolist (persistent-id psis) - (declare (PersistentIdC persistent-id)) - (setf (identified-construct persistent-id) instance)) - (dolist (subject-locator locators) - (declare (SubjectLocatorC subject-locator)) - (setf (identified-construct subject-locator) instance)) - (when reified - (setf (reified instance) reified))) - - -(defmethod delete-construct :before ((construct TopicC)) - (dolist (dependent (append (topic-identifiers construct) - (psis construct) - (locators construct) - (names construct) - (occurrences construct) - (player-in-roles construct) - (used-as-type construct))) - (delete-construct dependent)) - (dolist (theme (used-as-theme construct)) - (elephant:remove-association construct 'used-as-theme theme)) - (dolist (tm (in-topicmaps construct)) - (elephant:remove-association construct 'in-topicmaps tm)) - (when (reified construct) - (slot-makunbound (reified construct) 'reifier))) - -(defun get-all-constructs-by-uri (uri) - (delete - nil - (mapcar - (lambda (identifier) - (and - (slot-boundp identifier 'identified-construct) - (identified-construct identifier))) - (union - (union - (elephant:get-instances-by-value 'ItemIdentifierC 'uri uri) - (elephant:get-instances-by-value 'PersistentIdC 'uri uri)) - (elephant:get-instances-by-value 'SubjectLocatorC 'uri uri))))) - - -(defun find-existing-topic (item-identifiers locators psis) - (let - ((uris - (mapcar #'uri - (union (union item-identifiers locators) psis))) - (existing-topics nil)) - (dolist (uri uris) - (setf existing-topics - (nunion existing-topics - (get-all-constructs-by-uri uri) - :key #'internal-id))) - (assert (<= (length existing-topics) 1)) - (first existing-topics))) - - -(defmethod make-construct ((class-symbol (eql 'TopicC)) &rest args - &key start-revision item-identifiers locators psis topicid xtm-id) - (let - ((existing-topic - (find-existing-topic item-identifiers locators psis))) - (if existing-topic - (progn - ;our problem with topics is that we know only after the - ;addition of all the identifiers and characteristics if - ;anything has changed. We can't decide that here, so we must - ;add all revisions (real or imaginary) to version history - ;and decide the rest in changed-p. Maybe somebody can think - ;of a better way? - (add-to-version-history existing-topic - :start-revision start-revision) - (init-topic-identification existing-topic topicid xtm-id - :revision start-revision) - (let* ;add new identifiers to existing topics - ((all-new-identifiers - (union (union item-identifiers locators) psis)) - (all-existing-identifiers - (get-all-identifiers-of-construct existing-topic))) - (mapc - (lambda (identifier) - (setf (identified-construct identifier) existing-topic)) - (set-difference all-new-identifiers all-existing-identifiers - :key #'uri :test #'string=)) - (mapc #'delete-construct - (delete-if - (lambda (identifier) - (slot-boundp identifier 'identified-construct)) - all-new-identifiers))) - (check-for-duplicate-identifiers existing-topic) - existing-topic) - (progn - (let* - ((cleaned-args (remove-nil-values args)) - (new-topic - (apply #'make-instance 'TopicC cleaned-args))) - - (init-topic-identification new-topic topicid xtm-id - :revision start-revision) - (check-for-duplicate-identifiers new-topic) - (add-to-version-history new-topic - :start-revision start-revision) - new-topic))))) - -(defmethod make-construct :around ((class-symbol (eql 'TopicC)) - &key start-revision &allow-other-keys) - (declare (ignorable start-revision)) - (call-next-method)) - - -(defmethod equivalent-constructs ((topic1 TopicC) (topic2 TopicC)) - "TMDM, 5.3.5: Equality rule: Two topic items are equal if they have: - -* at least one equal string in their [subject identifiers] properties, - -* at least one equal string in their [item identifiers] properties, - -* at least one equal string in their [subject locators] properties, - -* an equal string in the [subject identifiers] property of the one -topic item and the [item identifiers] property of the other, or the -same information item in their [reified] properties (TODO: this rule -is currently ignored)" - ;(declare (optimize (debug 3))) - (let - ((psi-uris1 - (map 'list #'uri (psis topic1))) - (psi-uris2 - (map 'list #'uri (psis topic2))) - (ii-uris1 - (map 'list #'uri (item-identifiers topic1))) - (ii-uris2 - (map 'list #'uri (item-identifiers topic2))) - (locators1 - (map 'list #'uri (locators topic1))) - (locators2 - (map 'list #'uri (locators topic2)))) - (let - ((all-uris1 - (union psi-uris1 (union ii-uris1 locators1) :test #'string=)) - (all-uris2 - (union psi-uris2 (union ii-uris2 locators2) :test #'string=))) - ;;TODO: consider what we should do about this. If the topic at a - ;;given revision doesn't exist yet, it correctly has no uris - ;;(for that version) - ;; (when (= 0 (length all-uris1)) -;; (error (make-condition 'no-identifier-error :message "Topic1 has no identifier" :internal-id (internal-id topic1)))) -;; (when (= 0 (length all-uris2)) -;; (error (make-condition 'no-identifier-error :message "Topic2 has no identifier" :internal-id (internal-id topic2)))) - (intersection - all-uris1 all-uris2 - :test #'string=)))) - -(defmethod get-all-identifiers-of-construct ((top TopicC)) - (append (psis top) - (locators top) - (item-identifiers top))) - - -(defmethod topicid ((top TopicC) &optional (xtm-id nil)) - "Return the primary id of this item (= essentially the OID). If -xtm-id is explicitly given, return one of the topicids in that -TM (which must then exist)" - (if xtm-id - (let - ((possible-identifications - (remove-if-not - (lambda (top-id) - (string= (xtm-id top-id) xtm-id)) - (elephant:get-instances-by-value - 'TopicIdentificationC - 'identified-construct - top)))) - (unless possible-identifications - (error (make-condition - 'object-not-found-error - :message - (format nil "Could not find an object ~a in xtm-id ~a" top xtm-id)))) - (uri (first possible-identifications))) - (format nil "t~a" - (internal-id top)))) - - -(defgeneric psis-p (top) - (:documentation "Test for the existence of PSIs") - (:method ((top TopicC)) (slot-predicate top 'psis))) - -(defgeneric list-instanceOf (topic &key tm) - (:documentation "Generate a list of all topics that this topic is an - instance of, optionally filtered by a topic map")) - -(defmethod list-instanceOf ((topic TopicC) &key (tm nil)) - (remove-if - #'null - (map 'list #'(lambda(x) - (when (loop for psi in (psis (instance-of x)) - when (string= (uri psi) "http://psi.topicmaps.org/iso13250/model/instance") - return t) - (loop for role in (roles (parent x)) - when (not (eq role x)) - return (player role)))) - (if tm - (remove-if-not - (lambda (role) - ;(format t "player: ~a" (player role)) - ;(format t "parent: ~a" (parent role)) - ;(format t "topic: ~a~&" topic) - (in-topicmap tm (parent role))) - (player-in-roles topic)) - (player-in-roles topic))))) - - -(defgeneric list-super-types (topic &key tm) - (:documentation "Generate a list of all topics that this topic is an - subclass of, optionally filtered by a topic map")) - - -(defmethod list-super-types ((topic TopicC) &key (tm nil)) - (remove-if - #'null - (map 'list #'(lambda(x) - (when (loop for psi in (psis (instance-of x)) - when (string= (uri psi) *subtype-psi*) - return t) - (loop for role in (roles (parent x)) - when (not (eq role x)) - return (player role)))) - (if tm - (remove-if-not - (lambda (role) - (format t "player: ~a" (player role)) - (format t "parent: ~a" (parent role)) - (format t "topic: ~a~&" topic) - (in-topicmap tm (parent role))) - (player-in-roles topic)) - (player-in-roles topic))))) - - -(defun string-starts-with (str prefix) - "Checks if string str starts with a given prefix" - (declare (string str prefix)) - (string= str prefix :start1 0 :end1 - (min (length prefix) - (length str)))) - - -(defun get-item-by-item-identifier (uri &key revision) - "get a construct by its item identifier. Returns nil if the item does not exist in a -particular revision" - (declare (string uri)) - (declare (integer revision)) - (let - ((ii-obj - (elephant:get-instance-by-value 'ItemIdentifierC - 'uri uri))) - (when ii-obj - (find-item-by-revision - (identified-construct ii-obj) revision)))) - - -(defun get-item-by-psi (psi &key (revision 0)) - "get a topic by its PSI. Returns nil if the item does not exist in a -particular revision" - (declare (string psi)) - (declare (integer revision)) - (let - ((psi-obj - (elephant:get-instance-by-value 'PersistentIdC - 'uri psi))) - (when psi-obj - (find-item-by-revision - (identified-construct psi-obj) revision)))) - -(defun get-item-by-id (topicid &key (xtm-id *current-xtm*) (revision 0) (error-if-nil nil)) - "get a topic by its id, assuming a xtm-id. If xtm-id is empty, the current TM -is chosen. If xtm-id is nil, choose the global TM with its internal ID, if -applicable in the correct revision. If revison is provided, then the code checks -if the topic already existed in this revision and returns nil otherwise. -If no item meeting the constraints was found, then the return value is either -NIL or an error is thrown, depending on error-if-nil." - (declare (integer revision)) - (let - ((result - (if xtm-id - (let - ((possible-items - (delete-if-not - (lambda (top-id) - (and - (string= (xtm-id top-id) xtm-id) - (string= (uri top-id) topicid))) ;fixes a bug in - ;get-instances-by-value - ;that does a - ;case-insensitive - ;comparision - (elephant:get-instances-by-value - 'TopicIdentificationC - 'uri - topicid)))) - (when (and possible-items - (identified-construct-p (first possible-items))) - (unless (= (length possible-items) 1) - (error (make-condition 'duplicate-identifier-error - :message - (format nil "(length possible-items ~a) for id ~a und xtm-id ~a > 1" possible-items topicid xtm-id) - :uri topicid))) - (let - ((found-topic - (identified-construct (first possible-items)))) - (if (= revision 0) - found-topic - (find-item-by-revision found-topic revision))))) - (make-instance 'TopicC :from-oid (subseq topicid 1))))) - (if (and error-if-nil (not result)) - (error (format nil "no such item (id: ~a, tm: ~a, rev: ~a)" topicid xtm-id revision)) - result))) - - -;;;;;;;;;;;;;;;;;; -;; -;; RoleC - -(elephant:defpclass RoleC (ReifiableConstructC TypableC) - ((parent :accessor parent - :initarg :parent - :associate AssociationC - :documentation "Association that this role belongs to") - (player :accessor player - :initarg :player - :associate TopicC - :documentation "references the topic that is the player in this role")) - (:documentation "The role that this topic plays in an association (formerly member)")) - - - -(defgeneric RoleC-p (object) - (:documentation "test if object is a of type RoleC") - (:method ((object t)) nil) - (:method ((object RoleC)) object)) - - -(defgeneric parent-p (vi) - (:documentation "t if this construct has a parent construct") - (:method ((constr RoleC)) (slot-predicate constr 'parent))) - - -(defmethod delete-construct :before ((construct RoleC)) - ;the way we use roles, we cannot just delete the parent association - ;(at least the second role won't have one left then and will - ;complain) - (delete-1-n-association construct 'parent) - (delete-1-n-association construct 'player)) - -(defmethod find-all-equivalent ((construct RoleC)) - (let - ((parent (and (slot-boundp construct 'parent) - (parent construct)))) - (when parent - (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) - (slot-value parent 'roles))))) - - -(defmethod equivalent-constructs ((role1 RoleC) (role2 RoleC)) - "Association role items are equal if the values of their [type], [player], and [parent] properties are equal (TMDM 5.8)" - ;for the purposes for which we use this method (namely the - ;construction of associations), roles will initially always be - ;unequal regarding their parent properties - (and - (= (internal-id (instance-of role1)) (internal-id (instance-of role2))) - (= (internal-id (player role1)) (internal-id (player role2))))) - - -;;;;;;;;;;;;;;;;;; -;; -;; AssociationC - -(elephant:defpclass AssociationC (ReifiableConstructC ScopableC TypableC) - ((roles :accessor roles - :associate (RoleC parent) - :documentation "(non-empty) list of this association's roles") - (in-topicmaps - :associate (TopicMapC associations) - :many-to-many t - :documentation "list of all topic maps this association is part of")) - (:documentation "Association in a Topic Map") - (:index t)) - - -(defmethod in-topicmaps ((association AssociationC) &key (revision *TM-REVISION*)) - (filter-slot-value-by-revision association 'in-topicmaps :start-revision revision)) - - -(defgeneric AssociationC-p (object) - (:documentation "test if object is a of type AssociationC") - (:method ((object t)) nil) - (:method ((object AssociationC)) object)) - - -(defmethod initialize-instance :around ((instance AssociationC) - &key - (roles nil)) - "implements the pseudo-initarg :roles" - (declare (list roles)) - (let - ((association (call-next-method))) - (dolist (role-data roles) - (make-instance - 'RoleC - :instance-of (getf role-data :instance-of) - :player (getf role-data :player) - :item-identifiers (getf role-data :item-identifiers) - :reifier (getf role-data :reifier) - :parent association)))) - -(defmethod make-construct :around ((class-symbol (eql 'AssociationC)) - &key - start-revision - &allow-other-keys) - (declare (ignorable start-revision)) - (let - ((association - (call-next-method))) - (declare (AssociationC association)) - (dolist (role (slot-value association 'roles)) - (unless (versions role) - (add-to-version-history role - :start-revision start-revision))) - association)) - -(defmethod copy-item-identifiers :around - ((from-construct AssociationC) - (to-construct AssociationC)) - "Internal method to copy over item idenfiers from one association -with its roles to another one. Role identifiers are also -copied. Returns nil if neither association nor role identifiers had to be copied" - (let - ((item-identifiers-copied-p nil)) ;rather brutal solution. find a better one - (when (call-next-method) - (setf item-identifiers-copied-p t)) - (do ((from-roles (roles from-construct) (rest from-roles)) - (to-roles (roles to-construct) (rest to-roles))) - ((null from-roles) 'finished) - (let - ((from-role (first from-roles)) - (to-role (first to-roles))) - (when - (mapc - (lambda (identifier) - (setf (identified-construct identifier) - to-role)) - (set-difference (item-identifiers from-role) - (item-identifiers to-role) - :key #'uri :test #'string=)) - (setf item-identifiers-copied-p t)))) - item-identifiers-copied-p)) - -(defmethod delete-construct :before ((construct AssociationC)) - (dolist (role (roles construct)) - (delete-construct role)) - (dolist (tm (in-topicmaps construct)) - (elephant:remove-association construct 'in-topicmaps tm))) - -(defmethod find-all-equivalent ((construct AssociationC)) - (let - ((some-player (player (or - (second (roles construct)) - (first (roles construct)))))) ;; dirty, dirty... but brings a tenfold speedup! - (delete-if-not - #'(lambda (cand) - (unless (eq construct cand) - (equivalent-constructs construct cand))) - ;here we need to use the "internal" API and access the players - ;with slot-value (otherwise we won't be able to merge with - ;'deleted' associations) - (mapcar #'parent (slot-value some-player 'player-in-roles))))) - - -(defmethod equivalent-constructs ((assoc1 AssociationC) (assoc2 AssociationC)) - "Association items are equal if the values of their [scope], [type], and [roles] properties are equal (TMDM 5.7)" - (and - (= (internal-id (instance-of assoc1)) (internal-id (instance-of assoc2))) - (not (set-exclusive-or (themes assoc1) (themes assoc2) - :key #'internal-id)) - (not (set-exclusive-or - (roles assoc1) - (roles assoc2) - :test #'equivalent-constructs)))) - - -(elephant:defpclass TopicMapC (ReifiableConstructC) - ((topics :accessor topics - :associate (TopicC in-topicmaps) - :documentation "list of topics that explicitly belong to this TM") - (associations :accessor associations - :associate (AssociationC in-topicmaps) - :documentation "list of associations that belong to this TM")) - (:documentation "Topic Map")) - -(defmethod equivalent-constructs ((tm1 TopicMapC) (tm2 TopicMapC)) - "Topic Map items are equal if one of their identifiers is equal" - ;Note: TMDM does not make any statement to this effect, but it's the - ;one logical assumption - (intersection - (item-identifiers tm1) - (item-identifiers tm2) - :test #'equivalent-constructs)) - -(defmethod find-all-equivalent ((construct TopicMapC)) - (let - ((tms (elephant:get-instances-by-class 'd:TopicMapC))) - (delete-if-not - (lambda(tm) - (strictly-equivalent-constructs construct tm)) - tms))) - -(defgeneric add-to-topicmap (tm top) - (:documentation "add a topic or an association to a topic - map. Return the added construct")) - -(defmethod add-to-topicmap ((tm TopicMapC) (top TopicC)) - ;TODO: add logic not to add pure topic stubs unless they don't exist yet in the store -; (elephant:add-association tm 'topics top) ;by adding the elephant association in this order, there will be missing one site of this association - (elephant:add-association top 'in-topicmaps tm) - top) - -(defmethod add-to-topicmap ((tm TopicMapC) (ass AssociationC)) - ;(elephant:add-association tm 'associations ass) - (elephant:add-association ass 'in-topicmaps tm) - ass) - -(defgeneric in-topicmap (tm constr &key revision) - (:documentation "Is a given construct (topic or assiciation) in this topic map?")) - -(defmethod in-topicmap ((tm TopicMapC) (top TopicC) &key (revision 0)) - (when (find-item-by-revision top revision) - (find (d:internal-id top) (d:topics tm) :test #'= :key #'d:internal-id))) - - -(defmethod in-topicmap ((tm TopicMapC) (ass AssociationC) &key (revision 0)) - (when (find-item-by-revision ass revision) - (find (d:internal-id ass) (d:associations tm) :test #'= :key #'d:internal-id))) - -;;;;;;;;;;;;;;;;; -;; reification - -(defgeneric add-reifier (construct reifier-topic) - (:method ((construct ReifiableConstructC) reifier-topic) - (let ((err "From add-reifier(): ")) - (declare (TopicC reifier-topic)) - (cond - ((and (not (reifier construct)) - (not (reified reifier-topic))) - (setf (reifier construct) reifier-topic) - (setf (reified reifier-topic) construct)) - ((and (not (reified reifier-topic)) - (reifier construct)) - (merge-reifier-topics (reifier construct) reifier-topic)) - ((and (not (reifier construct)) - (reified reifier-topic)) - (error "~a~a ~a reifies already another object ~a" - err (psis reifier-topic) (item-identifiers reifier-topic) - (reified reifier-topic))) - (t - (when (not (eql (reified reifier-topic) construct)) - (error "~a~a ~a reifies already another object ~a" - err (psis reifier-topic) (item-identifiers reifier-topic) - (reified reifier-topic))) - (merge-reifier-topics (reifier construct) reifier-topic))) - construct))) - - -(defgeneric remove-reifier (construct) - (:method ((construct ReifiableConstructC)) - (let ((reifier-topic (reifier construct))) - (when reifier-topic - (elephant:remove-association construct 'reifier reifier-topic) - (elephant:remove-association reifier-topic 'reified construct))))) - - -(defgeneric merge-reifier-topics (old-topic new-topic) - ;;the reifier topics are not only merged but also bound to the reified-construct - (:method ((old-topic TopicC) (new-topic TopicC)) - (unless (eql old-topic new-topic) - ;merges all identifiers - (move-identifiers old-topic new-topic) - (move-identifiers old-topic new-topic :what 'locators) - (move-identifiers old-topic new-topic :what 'psis) - (move-identifiers old-topic new-topic :what 'topic-identifiers) - ;merges all typed-object-associations - (dolist (typed-construct (used-as-type new-topic)) - (remove-association typed-construct 'instance-of new-topic) - (add-association typed-construct 'instance-of old-topic)) - ;merges all scope-object-associations - (dolist (scoped-construct (used-as-theme new-topic)) - (remove-association scoped-construct 'themes new-topic) - (add-association scoped-construct 'themes old-topic)) - ;merges all topic-maps - (dolist (tm (in-topicmaps new-topic)) - (add-association tm 'topics old-topic)) ;the new-topic is removed from this tm by deleting it - ;merges all role-players - (dolist (a-role (player-in-roles new-topic)) - (remove-association a-role 'player new-topic) - (add-association a-role 'player old-topic)) - ;merges all names - (dolist (name (names new-topic)) - (remove-association name 'topic new-topic) - (add-association name 'topic old-topic)) - ;merges all occurrences - (dolist (occurrence (occurrences new-topic)) - (remove-association occurrence 'topic new-topic) - (add-association occurrence 'topic old-topic)) - ;merges all version-infos - (let ((versions-to-move - (loop for vrs in (versions new-topic) - when (not (find-if #'(lambda(x) - (and (= (start-revision x) (start-revision vrs)) - (= (end-revision x) (end-revision vrs)))) - (versions old-topic))) - collect vrs))) - (dolist (vrs versions-to-move) - (remove-association vrs 'versioned-construct new-topic) - (add-association vrs 'versioned-construct old-topic))) - (delete-construct new-topic)) - ;TODO: order/repair all version-infos of the topic itself and add all new - ; versions to the original existing objects of the topic - old-topic)) \ No newline at end of file +;; ;; functions and slot accessors +;; :in-topicmaps +;; :add-to-topicmap +;; :add-source-locator +;; :associations +;; :changed-p +;; :charvalue +;; :check-for-duplicate-identifiers +;; :datatype +;; :equivalent-constructs +;; :find-item-by-revision +;; :find-most-recent-revision +;; :get-all-revisions +;; :get-all-revisions-for-tm +;; :get-fragment +;; :get-fragments +;; :get-revision +;; :get-item-by-content +;; :get-item-by-id +;; :get-item-by-item-identifier +;; :get-item-by-psi +;; :identified-construct +;; :identified-construct-p +;; :in-topicmap +;; :internal-id +;; :instance-of +;; :instance-of-p +;; :item-identifiers +;; :item-identifiers-p +;; :list-instanceOf +;; :list-super-types +;; :locators +;; :locators-p +;; :make-construct +;; :mark-as-deleted +;; :names +;; :namevalue +;; :occurrences +;; :name +;; :parent +;; :player +;; :player-in-roles +;; :players +;; :psis +;; :psis-p +;; :referenced-topics +;; :revision +;; :RoleC-p +;; :roleid +;; :roles +;; :themes +;; :xtm-id +;; :xtm-id-p +;; :topic +;; :topicid +;; :topic-identifiers +;; :topics +;; :unique-id +;; :uri +;; :uri-p +;; :used-as-type +;; :used-as-theme +;; :variants +;; :xor +;; :create-latest-fragment-of-topic +;; :reified +;; :reifier +;; :add-reifier +;; :remove-reifier +;; +;; :*current-xtm* ;; special variables +;; :*TM-REVISION* +;; +;; :with-revision ;;macros +;; +;; :string-starts-with ;;helpers +;; )) +;; +;;(declaim (optimize (debug 3) (safety 3) (speed 0) (space 0))) +;;(in-package :datamodel) +;; +;;(defparameter *current-xtm* nil "Represents the currently active TM") +;; +;;(defmacro find-max-elem (candidate-list &key (relop #'> relop-p) (key #'identity key-p)) +;; "Given a non-empty list, return the maximum element in the list. +;; If provided, then relop must be a relational operator that determines the ordering; +;; else #'> is used. The keyword parameter key may name a function that is used to extract +;; the sort key; otherwise the elements themselves are the sort keys." +;; (let +;; ((candidate-list-value-name (gensym)) +;; (relop-value-name (gensym)) +;; (key-value-name (gensym)) +;; (best-seen-cand-name (gensym)) +;; (max-key-name (gensym)) +;; (inspected-cand-name (gensym)) +;; (inspected-key-name (gensym))) +;; (let +;; ((max-key-init (if key-p +;; `(funcall ,key-value-name ,best-seen-cand-name) +;; best-seen-cand-name)) +;; (inspected-key-init (if key-p +;; `(funcall ,key-value-name ,inspected-cand-name) +;; inspected-cand-name)) +;; (relexp (if relop-p +;; `(funcall ,relop-value-name ,inspected-key-name ,max-key-name) +;; `(> ,inspected-key-name ,max-key-name)))) +;; (let +;; ((initializers `((,candidate-list-value-name ,candidate-list) +;; (,best-seen-cand-name (first ,candidate-list-value-name)) +;; (,max-key-name ,max-key-init)))) +;; (when relop-p +;; (push `(,relop-value-name ,relop) initializers)) +;; (when key-p +;; (push `(,key-value-name ,key) initializers)) +;; `(let* +;; ,initializers +;; (dolist (,inspected-cand-name (rest ,candidate-list-value-name)) +;; (let +;; ((,inspected-key-name ,inspected-key-init)) +;; (when ,relexp +;; (setf ,best-seen-cand-name ,inspected-cand-name) +;; (setf ,max-key-name ,inspected-key-name)))) +;; ,best-seen-cand-name))))) +;; +;;(defvar *TM-REVISION* 0) +;; +;;(defmacro with-revision (revision &rest body) +;; `(let +;; ((*TM-REVISION* ,revision)) +;; ;(format t "*TM-REVISION* is ~a~&" *TM-REVISION*) +;; ,@body)) +;; +;; +;;(defmacro slot-predicate (instance slot) +;; (let +;; ((inst-name (gensym)) +;; (slot-name (gensym))) +;; `(let +;; ((,inst-name ,instance) +;; (,slot-name ,slot)) +;; (and (slot-boundp ,inst-name ,slot-name) +;; (slot-value ,inst-name ,slot-name))))) +;; +;;(defmacro delete-1-n-association (instance slot) +;; (let +;; ((inst-name (gensym)) +;; (slot-name (gensym))) +;; `(let +;; ((,inst-name ,instance) +;; (,slot-name ,slot)) +;; (when (slot-predicate ,inst-name ,slot-name) +;; (elephant:remove-association ,inst-name ,slot-name (slot-value ,inst-name ,slot-name)))))) +;; +;;(defun xor (a1 a2) +;; (and (or a1 a2) (not (and a1 a2))) +;; ) +;; +;;(defun remove-nil-values (plist) +;; (let +;; ((result nil)) +;; (do* ((rest plist (cddr rest)) +;; (key (first rest) (first rest)) +;; (val (second rest) (second rest))) +;; ((null rest)) +;; (when val +;; (pushnew val result) +;; (pushnew key result))) +;; result)) +;; +;;(defun get-revision () +;; "TODO: replace by something that does not suffer from a 1 second resolution." +;; (get-universal-time)) +;; +;;(defgeneric delete-construct (construct) +;; (:documentation "drops recursively construct and all its dependent objects from the elephant store")) +;; +;;(defmethod delete-construct ((construct elephant:persistent)) +;; nil) +;; +;;(defmethod delete-construct :after ((construct elephant:persistent)) +;; (elephant:drop-instance construct)) +;; +;;(defgeneric find-all-equivalent (construct) +;; (:method ((construct t)) nil) +;; (:documentation "searches an existing object that is equivalent (but not identical) to construct")) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; VersionInfoC +;; +;; +;;(elephant:defpclass VersionInfoC () +;; ((start-revision :accessor start-revision +;; :initarg :start-revision +;; :type integer +;; :initform 0 ;TODO: for now +;; :documentation "The first revison this AssociationC instance is associated with.") +;; (end-revision :accessor end-revision +;; :initarg :end-revision +;; :type integer +;; :initform 0 ;TODO: for now +;; :documentation "The first revison this AssociationC instance is no longer associated with.") +;; (versioned-construct :associate TopicMapConstructC +;; :accessor versioned-construct +;; :initarg :versioned-construct +;; :documentation "reifiable construct that is described by this info")) +;; (:documentation "Version Info for individual revisions")) +;; +;;(defgeneric versioned-construct-p (vi) +;; (:documentation "t if this version info is already bound to a TM construct") +;; (:method ((vi VersionInfoC)) (slot-predicate vi 'versioned-construct))) +;; +;;(defmethod delete-construct :before ((vi VersionInfoC)) +;; (delete-1-n-association vi 'versioned-construct)) +;; +;;(defgeneric get-most-recent-version-info (construct)) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; ItemIdentifierC +;; +;;(elephant:defpclass ItemIdentifierC (IdentifierC) +;; () +;; (:index t) +;; (:documentation "Represents an item identifier")) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; SubjectLocator +;; +;;(elephant:defpclass SubjectLocatorC (IdentifierC) +;; ((identified-construct :accessor identified-construct +;; :initarg :identified-construct +;; :associate TopicC)) +;; (:index t) +;; (:documentation "Represents a subject locator")) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; IdentifierC +;; +;;(elephant:defpclass IdentifierC (PointerC) +;; () +;; (:documentation "Abstract base class for ItemIdentifierC and +;; PersistentIdC, primarily in view of the equality rules")) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; PointerC +;; +;;(elephant:defpclass PointerC (TopicMapConstructC) +;; ((uri :accessor uri +;; :initarg :uri +;; :type string +;; :initform (error "The uri must be set for a pointer") +;; :index t) +;; (identified-construct :accessor identified-construct +;; :initarg :identified-construct +;; :associate ReifiableConstructC)) +;; (:documentation "Abstract base class for all types of pointers and identifiers")) +;; +;;(defmethod delete-construct :before ((construct PointerC)) +;; (delete-1-n-association construct 'identified-construct)) +;; +;;(defmethod find-all-equivalent ((construct PointerC)) +;; (delete construct +;; (elephant:get-instances-by-value (class-of construct) +;; 'uri +;; (uri construct)) +;; :key #'internal-id)) +;;(defgeneric uri-p (construct) +;; (:documentation "Check if the slot uri is bound in an identifier and not nil") +;; (:method ((identifier PointerC)) (slot-predicate identifier 'uri))) +;; +;;(defgeneric identified-construct-p (construct) +;; (:documentation "Check if the slot identified-construct is bound in an identifier and not nil") +;; (:method ((identifier PointerC)) (slot-predicate identifier 'identified-construct))) +;; +;;(defmethod print-object ((identifier PointerC) stream) +;; (format stream +;; "~a(href: ~a; Construct: ~a)" +;; (class-name (class-of identifier)) +;; (if (uri-p identifier) +;; (uri identifier) +;; "URI UNDEFINED") +;; (if (identified-construct-p identifier) +;; (identified-construct identifier) +;; "SLOT UNBOUND"))) +;; +;;(defmethod equivalent-constructs ((identifier1 PointerC) (identifier2 PointerC)) +;; (string= (uri identifier1) (uri identifier2))) +;; +;;(defmethod initialize-instance :around ((identifier PointerC) &key +;; (start-revision (error "Start revision must be present") ) +;; (end-revision 0)) +;; (call-next-method) +;; (add-to-version-history identifier +;; :start-revision start-revision +;; :end-revision end-revision) +;; identifier) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; TopicMapConstrucC +;; +;; +;;(elephant:defpclass TopicMapConstructC () +;; ((versions :associate (VersionInfoC versioned-construct) +;; :accessor versions +;; :initarg :versions +;; :documentation "version infos for former versions of this reifiable construct"))) +;; +;; ;TODO: if, one day, we allow merges of already existing constructs, we'll need +;; ;a tree of predecessors rather then just a list of versions. A case in point +;; ;may be if a newly imported topic carries the PSIs of two existing topics, +;; ;thereby forcing a merge post factum" +;; +;;(defmethod delete-construct :before ((construct TopicMapConstructC)) +;; (dolist (versioninfo (versions construct)) +;; (delete-construct versioninfo))) +;; +;; +;;(defgeneric add-to-version-history (construct &key start-revision end-revision) +;; (:documentation "Add version history to a topic map construct")) +;; +;;(defmethod add-to-version-history ((construct TopicMapConstructC) +;; &key +;; (start-revision (error "Start revision must be present") ) +;; (end-revision 0)) +;; "Adds relevant information to a construct's version info" +;; (let +;; ((current-version-info +;; (get-most-recent-version-info construct))) +;; (cond +;; ((and current-version-info +;; (= (end-revision current-version-info) start-revision)) ;the item was just marked as deleted +;; (setf (end-revision current-version-info) 0) ;just revitalize it, do not create a new version +;; current-version-info) ;TODO: this is not quite correct, the topic +;; ;might be recreated with new item +;; ;identifiers. Consider adding a new parameter +;; ;"revitalize" +;; ((and +;; current-version-info +;; (= (end-revision current-version-info) 0)) +;; (setf (end-revision current-version-info) start-revision) +;; (make-instance +;; 'VersionInfoC +;; :start-revision start-revision +;; :end-revision end-revision +;; :versioned-construct construct)) +;; (t +;; (make-instance +;; 'VersionInfoC +;; :start-revision start-revision +;; :end-revision end-revision +;; :versioned-construct construct))))) +;; +;;(defgeneric revision (constr) +;; (:documentation "Essentially a convenience method for start-revision")) +;; +;;(defmethod revision ((constr TopicMapConstructC)) +;; (start-revision constr)) +;; +;;(defmethod (setf revision) ((constr TopicMapConstructC) (revision integer)) +;; (setf (start-revision constr) revision)) +;; +;; +;;(defgeneric find-item-by-revision (constr revision) +;; (:documentation "Get a given version of a construct (if any, nil if none can be found)")) +;; +;;(defmethod find-item-by-revision ((constr TopicMapConstructC) (revision integer)) +;; (cond +;; ((= revision 0) +;; (find-most-recent-revision constr)) +;; (t +;; (when (find-if +;; (lambda(version) +;; (and (>= revision (start-revision version)) +;; (or +;; (< revision (end-revision version)) +;; (= 0 (end-revision version))))) +;; (versions constr)) +;; constr)))) +;; +;;(defgeneric find-most-recent-revision (construct) +;; (:documentation "Get the most recent version of a construct (nil if +;;the construct doesn't have versions yet or not anymore)")) +;; +;;(defmethod find-most-recent-revision ((construct TopicMapConstructC)) +;; (when (find 0 (versions construct) :key #'end-revision) +;; construct)) +;; +;;(defmethod delete-construct :before ((construct TopicMapConstructC)) +;; (dolist (versionInfo (versions construct)) +;; (delete-construct versionInfo))) +;; +;; +;;(defgeneric check-for-duplicate-identifiers (top) +;; (:documentation "Check for possibly duplicate identifiers and signal an +;; duplicate-identifier-error is such duplicates are found")) +;; +;;(defmethod check-for-duplicate-identifiers ((construct TopicMapConstructC)) +;; (declare (ignore construct)) +;; ;do nothing +;; ) +;; +;;(defgeneric filter-slot-value-by-revision (construct slot-name &key start-revision) +;; (:documentation "filter slot values by a given revision that is +;; either provided directly through the keyword argument start-revision +;; or through a bound variable named '*TM-REVISION*'")) +;; +;;(defmethod filter-slot-value-by-revision ((construct TopicMapConstructC) (slot-name symbol) &key (start-revision 0 start-revision-provided-p)) +;; (let +;; ((revision ;avoids warnings about undefined variables +;; (cond +;; (start-revision-provided-p +;; start-revision) +;; ((boundp '*TM-REVISION*) +;; (symbol-value '*TM-REVISION*)) +;; (t 0))) +;; (properties (slot-value construct slot-name))) +;; ;(format t "revision in filter-slot-value-by-revision is ~a~&" revision) +;; (cond +;; ((not properties) +;; nil) ;if we don't have any properties, we don't have to worry +;; ;about revisions +;; ((= 0 revision) +;; (remove +;; nil +;; (map 'list #'find-most-recent-revision +;; properties))) +;; (t +;; (remove nil +;; (map 'list +;; (lambda (constr) +;; (find-item-by-revision constr revision)) +;; properties)))))) +;; +;;(defgeneric make-construct (classsymbol &key start-revision &allow-other-keys) +;; (:documentation "create a new topic map construct if necessary or +;;retrieve an equivalent one if available and update the revision +;;history accordingly. Return the object in question. Methods use +;;specific keyword arguments for their purpose")) +;; +;;(defmethod make-construct ((classsymbol symbol) &rest args +;; &key start-revision) +;; (let* +;; ((cleaned-args (remove-nil-values args)) +;; (new-construct (apply #'make-instance classsymbol cleaned-args)) +;; (existing-construct (first (find-all-equivalent new-construct)))) +;; (if existing-construct +;; (progn +;; ;change over new item identifiers to the old construct +;; (when (copy-item-identifiers +;; new-construct existing-construct) +;; ;an existing construct other than a topic (which is handled +;; ;separatedly below) has changed only if it has received a new +;; ;item identifier +;; (add-to-version-history existing-construct :start-revision start-revision)) +;; (delete-construct new-construct) +;; existing-construct) +;; (progn +;; (add-to-version-history new-construct :start-revision start-revision) +;; (check-for-duplicate-identifiers new-construct) +;; new-construct)))) +;; +;;(defmethod get-most-recent-version-info ((construct TopicMapConstructC)) +;; (let ((result (find 0 (versions construct) :key #'end-revision))) +;; (if result +;; result ;current version-info -> end-revision = 0 +;; (let ((sorted-list (sort (versions construct) +;; #'(lambda(x y) +;; (> (end-revision x) (end-revision y)))))) +;; (when sorted-list +;; (first sorted-list)))))) ;latest version-info of marked-as-deleted constructs -> highest integer +;; +;;(defgeneric equivalent-constructs (construct1 construct2) +;; (:documentation "checks if two topic map constructs are equal according to the TMDM equality rules")) +;; +;;(defgeneric strictly-equivalent-constructs (construct1 construct2) +;; (:documentation "checks if two topic map constructs are not identical but equal according to the TMDM equality rules") +;; (:method ((construct1 TopicMapConstructC) (construct2 TopicMapConstructC)) +;; (and (equivalent-constructs construct1 construct2) +;; (not (eq construct1 construct2))))) +;; +;;(defgeneric internal-id (construct) +;; (:documentation "returns the internal id that uniquely identifies a +;; construct (currently simply its OID)")) +;; +;;(defmethod internal-id ((construct TopicMapConstructC)) +;; (slot-value construct (find-symbol "OID" 'elephant))) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; TopicIdentificationC +;; +;;(elephant:defpclass TopicIdentificationC (PointerC) +;; ((xtm-id +;; :accessor xtm-id +;; :type string +;; :initarg :xtm-id +;; :index t +;; :documentation "ID of the TM this identification came from")) +;; (:documentation "Identify topic items through generalized +;; topicids. A topic may have many original topicids, the class +;; representing one of them") ) +;; +;;(defmethod find-all-equivalent ((construct TopicIdentificationC)) +;; (delete (xtm-id construct) (call-next-method) :key #'xtm-id :test #'string=)) +;; +;;(defun init-topic-identification (top id xtm-id &key (revision *TM-REVISION*)) +;; "create a TopicIdentification object (if necessary) and initialize it with the +;; combination of the current topicid and the ID of the current XTM id" +;; ;(declare (TopicC top)) +;; (declare (string id)) +;; +;; (flet ;prevent unnecessary copies of TopicIdentificationC objects +;; ((has-topic-identifier (top uri xtm-id) +;; (remove-if-not +;; (lambda (ident) +;; (and (string= (uri ident) uri) +;; (string= (xtm-id ident) xtm-id))) +;; (topic-identifiers top)))) +;; (unless (has-topic-identifier top id xtm-id) +;; (let +;; ((ti +;; (make-instance +;; 'TopicIdentificationC +;; :uri id +;; :xtm-id xtm-id +;; :identified-construct top +;; :start-revision revision))) +;; ;(add-to-version-history ti :start-revision revision) +;; ti)))) +;; +;;(defun xtm-id-p (xtm-id) +;; "checks if a xtm-id has been used before" +;; (elephant:get-instance-by-value 'TopicIdentificationC +;; 'xtm-id xtm-id)) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; PSI +;; +;;(elephant:defpclass PersistentIdC (IdentifierC) +;; ((identified-construct :accessor identified-construct +;; :initarg :identified-construct +;; :associate TopicC)) +;; (:index t) +;; (:documentation "Represents a PSI")) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; ReifiableConstructC +;; +;;(elephant:defpclass ReifiableConstructC (TopicMapConstructC) +;; ((item-identifiers +;; :associate (ItemIdentifierC identified-construct) +;; :inherit t +;; :documentation "Slot that realizes a 1 to N +;; relation between reifiable constructs and their +;; identifiers; pseudo-initarg is :item-identifiers. Is inherited by all reifiable constructs") +;; (reifier +;; :associate TopicC +;; :inherit t +;; :documentation "Represents a reifier association to a topic, i.e. +;; it stands for a 1:1 association between this class and TopicC")) +;; (:documentation "Reifiable constructs as per TMDM")) +;; +;; +;;(defgeneric reifier (construct &key revision) +;; (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) +;; (when (slot-boundp construct 'reifier) +;; (slot-value construct 'reifier)))) +;; +;;(defgeneric (setf reifier) (topic TopicC) +;; (:method (topic (construct ReifiableConstructC)) +;; (setf (slot-value construct 'reifier) topic))) +;;; (setf (reified topic) construct))) +;; +;;(defgeneric item-identifiers (construct &key revision) +;; (:method ((construct ReifiableConstructC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision construct 'item-identifiers :start-revision revision))) +;; +;;(defmethod initialize-instance :around ((instance ReifiableConstructC) &key (item-identifiers nil) (reifier nil)) +;; "adds associations to these ids after the instance was initialized." +;; (declare (list item-identifiers)) +;; (call-next-method) +;; (dolist (id item-identifiers) +;; (declare (ItemIdentifierC id)) +;; (setf (identified-construct id) instance)) +;; (when reifier +;; (add-reifier instance reifier)) +;; ;(setf (reifier instance) reifier)) +;; instance) +;; +;;(defmethod delete-construct :before ((construct ReifiableConstructC)) +;; (dolist (id (item-identifiers construct)) +;; (delete-construct id)) +;; (when (reifier construct) +;; (let ((reifier-topic (reifier construct))) +;; (remove-reifier construct) +;; (delete-construct reifier-topic)))) +;; +;;(defgeneric item-identifiers-p (constr) +;; (:documentation "Test for the existence of item identifiers") +;; (:method ((construct ReifiableConstructC)) (slot-predicate construct 'item-identifiers))) +;; +;;(defgeneric topicid (construct &optional xtm-id) +;; (:documentation "Return the ID of a construct")) +;; +;;(defmethod revision ((constr ReifiableConstructC)) +;; (start-revision constr)) +;; +;;(defgeneric (setf revision) (revision construct) +;; (:documentation "The corresponding setter method")) +;; +;;(defmethod (setf revision) ((revision integer) (constr ReifiableConstructC)) +;; (setf (start-revision constr) revision)) +;; +;;(defgeneric get-all-identifiers-of-construct (construct) +;; (:documentation "Get all identifiers that a given construct has")) +;; +;;(defmethod get-all-identifiers-of-construct ((construct ReifiableConstructC)) +;; (item-identifiers construct)) +;; +;;(defmethod check-for-duplicate-identifiers ((construct ReifiableConstructC)) +;; (dolist (id (get-all-identifiers-of-construct construct)) +;; (when (> (length +;; (union +;; (elephant:get-instances-by-value 'ItemIdentifierC 'uri (uri id)) +;; (union +;; (elephant:get-instances-by-value 'PersistentIdC 'uri (uri id)) +;; (elephant:get-instances-by-value 'SubjectLocatorC 'uri (uri id))))) +;; 1) +;; (error +;; (make-condition 'duplicate-identifier-error +;; :message (format nil "Duplicate Identifier ~a has been found" (uri id)) +;; :uri (uri id)))))) +;; +;;(defmethod copy-item-identifiers ((from-construct ReifiableConstructC) +;; (to-construct ReifiableConstructC)) +;; "Internal method to copy over item idenfiers from a construct to +;;another on. Returns the set of new identifiers" +;; (mapc +;; (lambda (identifier) +;; (setf (identified-construct identifier) +;; to-construct)) +;; (set-difference (item-identifiers from-construct) +;; (item-identifiers to-construct) +;; :key #'uri :test #'string=))) +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; ScopableC +;; +;;(elephant:defpclass ScopableC () +;; ((themes :accessor themes +;; :associate (TopicC used-as-theme) +;; :inherit t +;; :many-to-many t +;; :documentation "list of this scope's themes; pseudo-initarg is :themes"))) +;; +;;(defmethod initialize-instance :around ((instance ScopableC) &key (themes nil)) +;; (declare (list themes)) +;; (call-next-method) +;; (dolist (theme themes) +;; (elephant:add-association instance 'themes theme)) +;; instance) +;; +;;(defmethod delete-construct :before ((construct ScopableC)) +;; (dolist (theme (themes construct)) +;; (elephant:remove-association construct 'themes theme))) +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; TypableC +;; +;;(elephant:defpclass TypableC () +;; ((instance-of :accessor instance-of +;; :initarg :instance-of +;; :associate TopicC +;; :inherit t +;; :documentation "topic that this construct is an instance of"))) +;; +;;(defmethod delete-construct :before ((construct TypableC)) +;; (when (instance-of-p construct) +;; (elephant:remove-association construct 'instance-of (instance-of construct)))) +;; +;;(defgeneric instance-of-p (construct) +;; (:documentation "is the instance-of slot bound and not nil") +;; (:method ((construct TypableC)) (slot-predicate construct 'instance-of))) +;; +;; +;;;; (defmethod equivalent-constructs ((scope1 ScopeC) (scope2 ScopeC)) +;;;; "scopes are equal if their themes are equal" +;;;; (let +;;;; ((themes1 +;;;; (map 'list #'internal-id (themes scope1))) +;;;; (themes2 +;;;; (map 'list #'internal-id (themes scope2)))) +;;;; (not (set-exclusive-or themes1 themes2 :key #'internal-id)))) +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; CharacteristicC +;; +;; +;;(elephant:defpclass CharacteristicC (ReifiableConstructC ScopableC TypableC) +;; ((topic :accessor topic +;; :initarg :topic +;; :associate TopicC +;; :documentation "The topic that this characteristic belongs to") +;; (charvalue :accessor charvalue +;; :type string +;; :initarg :charvalue +;; :index t +;; :documentation "the value of the characteristic in the given scope")) +;; (:documentation "Scoped characteristic of a topic (meant to be used +;; as an abstract class)")) +;; +;;(defgeneric CharacteristicC-p (object) +;; (:documentation "test if object is a of type CharacteristicC") +;; (:method ((object t)) nil) +;; (:method ((object CharacteristicC)) object)) +;; +;;(defmethod delete-construct :before ((construct CharacteristicC)) +;; (delete-1-n-association construct 'topic)) +;; +;;(defun get-item-by-content (content &key (revision *TM-REVISION*)) +;; "Find characteristis by their (atomic) content" +;; (flet +;; ((get-existing-instances (classname) +;; (delete-if-not #'(lambda (constr) +;; (find-item-by-revision constr revision)) +;; (elephant:get-instances-by-value classname 'charvalue content)))) +;; (nconc (get-existing-instances 'OccurenceC) +;; (get-existing-instances 'NameC)))) +;; +;; +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; VariantC +;; +;;(elephant:defpclass VariantC (CharacteristicC) +;; ((datatype :accessor datatype +;; :initarg :datatype +;; :initform nil +;; :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)") +;; (name :accessor name +;; :initarg :name +;; :associate NameC +;; :documentation "references the NameC instance which is the owner of this element"))) +;; +;; +;;(defgeneric VariantC-p (object) +;; (:documentation "test if object is a of type VariantC") +;; (:method ((object t)) nil) +;; (:method ((object VariantC)) object)) +;; +;; +;;(defmethod delete-construct :before ((construct VariantC)) +;; (delete-1-n-association construct 'name)) +;; +;; +;;(defmethod find-all-equivalent ((construct VariantC)) +;; (let ((parent (and (slot-boundp construct 'name) +;; (name construct)))) +;; (when parent +;; (delete-if-not #'(lambda(x)(strictly-equivalent-constructs construct x)) +;; (slot-value parent 'variants))))) +;; +;; +;;(defmethod equivalent-constructs ((variant1 VariantC) (variant2 VariantC)) +;; "variant items are (TMDM(5.5)-)equal if the values of their +;; [value], [datatype], [scope], and [parent] properties are equal" +;; (and (string= (charvalue variant1) (charvalue variant2)) +;; (or (and (not (slot-boundp variant1 'datatype)) (not (slot-boundp variant2 'datatype))) +;; (and (slot-boundp variant1 'datatype) (slot-boundp variant2 'datatype) +;; (string= (datatype variant1) (datatype variant2)))) +;; (not (set-exclusive-or (themes variant1) (themes variant2) :key #'internal-id)))) +;; +;; +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; NameC +;; +;;(elephant:defpclass NameC (CharacteristicC) +;; ((variants ;:accessor variants +;; :associate (VariantC name))) +;; (:documentation "Scoped name of a topic")) +;; +;; +;;(defgeneric variants (name &key revision) +;; (:method ((name NameC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision name 'variants :start-revision revision))) +;; +;; +;;(defgeneric NameC-p (object) +;; (:documentation "test if object is a of type NameC") +;; (:method ((object t)) nil) +;; (:method ((object NameC)) object)) +;; +;; +;;(defmethod find-all-equivalent ((construct NameC)) +;; (let +;; ((parent (and (slot-boundp construct 'topic) +;; (topic construct)))) +;; (when parent +;; (delete-if-not +;; #'(lambda (cand) (strictly-equivalent-constructs construct cand)) +;; (slot-value parent 'names))))) +;; +;; +;;(defmethod delete-construct :before ((construct NameC)) +;; (dolist (variant (variants construct)) +;; (delete-construct variant))) +;; +;; +;;(defmethod equivalent-constructs ((name1 NameC) (name2 NameC)) +;; "check for the equlity of two names by the TMDM's equality +;;rules (5.4)" +;; (and +;; (string= (charvalue name1) (charvalue name2)) +;; (or (and (instance-of-p name1) +;; (instance-of-p name2) +;; (= (internal-id (instance-of name1)) +;; (internal-id (instance-of name2)))) +;; (and (not (instance-of-p name1)) (not (instance-of-p name2)))) +;; (not (set-exclusive-or (themes name1) (themes name2) :key #'internal-id)))) +;; +;; +;; +;; +;;;;;;;;;;;;;;;; +;;;; +;;;; OccurrenceC +;; +;;(elephant:defpclass OccurrenceC (CharacteristicC) +;; ((datatype :accessor datatype +;; :initarg :datatype +;; :initform nil +;; :documentation "The XML Schema datatype of the occurrencevalue (optional, always IRI for resourceRef)"))) +;; +;; +;;(defgeneric OccurrenceC-p (object) +;; (:documentation "test if object is a of type OccurrenceC") +;; (:method ((object t)) nil) +;; (:method ((object OccurrenceC)) object)) +;; +;;(defmethod find-all-equivalent ((construct OccurrenceC)) +;; (let +;; ((parent (and (slot-boundp construct 'topic) +;; (topic construct)))) +;; (when parent +;; (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) +;; (slot-value parent 'occurrences))))) +;; +;;(defmethod equivalent-constructs ((occ1 OccurrenceC) (occ2 OccurrenceC)) +;; "Occurrence items are equal if the values of their [value], [datatype], [scope], [type], and [parent] properties are equal (TMDM 5.6)" +;; (and +;; (string= (charvalue occ1) (charvalue occ2)) +;; (not (set-exclusive-or (themes occ1) (themes occ2) :key #'internal-id)) +;; (= (internal-id (topic occ1)) (internal-id (topic occ2))) +;; (or +;; (and (instance-of-p occ1) (instance-of-p occ2) +;; (= +;; (internal-id (instance-of occ1)) +;; (internal-id (instance-of occ2)))) +;; (and (not (instance-of-p occ1)) (not (instance-of-p occ2)))))) +;; +;; +;;;;;;;;;;;;;;;;;;; +;;;; +;;;; TopicC +;; +;;(elephant:defpclass TopicC (ReifiableConstructC) +;; ((topic-identifiers +;; :accessor topic-identifiers +;; :associate (TopicIdentificationC identified-construct)) +;; (psis ;accessor written below +;; :associate (PersistentIdC identified-construct) +;; :documentation "list of PSI objects associated with this +;; topic") +;; (locators +;; ;accessor written below +;; :associate (SubjectLocatorC identified-construct) +;; :documentation "an optional URL that (if given) means that this topic is a subject locator") +;; (names ;accessor written below +;; :associate (NameC topic) +;; :documentation "list of topic names (as TopicC objects)") +;; (occurrences ;accessor occurrences explicitly written below +;; :associate (OccurrenceC topic) +;; :documentation "list of occurrences (as OccurrenceC objects)") +;; (player-in-roles ;accessor player-in-roles written below +;; :associate (RoleC player) +;; :documentation "the list of all role instances where this topic is a player in") +;; (used-as-type ;accessor used-as-type written below +;; :associate (TypableC instance-of) +;; :documentation "list of all constructs that have this topic as their type") +;; (used-as-theme ;accessor used-as-theme written below +;; :associate (ScopableC themes) +;; :many-to-many t +;; :documentation "list of all scopable objects this topic is a theme in") +;; (in-topicmaps +;; :associate (TopicMapC topics) +;; :many-to-many t +;; :documentation "list of all topic maps this topic is part of") +;; (reified +;; :associate ReifiableConstructC +;; :documentation "contains a reified object, represented as 1:1 association")) +;; (:documentation "Topic in a Topic Map")) +;; +;; +;;(defgeneric reified (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (when (slot-boundp topic 'reified) +;; (slot-value topic 'reified)))) +;; +;;(defgeneric (setf reified) (reifiable ReifiableConstructC) +;; (:method (reifiable (topic TopicC)) +;; (setf (slot-value topic 'reified) reifiable))) +;;; (setf (reifier reifiable) topic))) +;; +;;(defgeneric occurrences (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision topic 'occurrences :start-revision revision))) +;; +;;(defgeneric names (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision topic 'names :start-revision revision))) +;; +;;(defgeneric psis (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision +;; topic 'psis :start-revision revision))) +;; +;;(defgeneric locators (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision +;; topic 'locators :start-revision revision))) +;; +;;(defgeneric player-in-roles (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision +;; topic 'player-in-roles :start-revision revision))) +;; +;;(defgeneric used-as-type (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision topic 'used-as-type :start-revision revision))) +;; +;;(defgeneric used-as-theme (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision topic 'used-as-theme :start-revision revision))) +;; +;;(defgeneric in-topicmaps (topic &key revision) +;; (:method ((topic TopicC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision topic 'in-topicmaps :start-revision revision))) +;; +;;(defun move-identifiers(destination-topic source-topic &key (what 'item-identifiers)) +;; "Moves all identifiers from the source-topic to the destination topic." +;; (declare (TopicC destination-topic source-topic)) +;; (let ((all-source-identifiers +;; (cond +;; ((eql what 'item-identifiers) +;; (item-identifiers source-topic)) +;; ((eql what 'locators) +;; (locators source-topic)) +;; (t +;; (psis source-topic)))) +;; (all-destination-identifiers +;; (cond +;; ((eql what 'item-identifiers) +;; (item-identifiers destination-topic)) +;; ((eql what 'locators) +;; (locators destination-topic)) +;; ((eql what 'psis) +;; (psis destination-topic)) +;; ((eql what 'topic-identifiers) +;; (topic-identifiers destination-topic))))) +;; (let ((identifiers-to-move +;; (loop for id in all-source-identifiers +;; when (not (find-if #'(lambda(x) +;; (if (eql what 'topic-identifiers) +;; (string= (xtm-id x) (xtm-id id)) +;; (string= (uri x) (uri id)))) +;; all-destination-identifiers)) +;; collect id))) +;; (dolist (item identifiers-to-move) +;; (remove-association source-topic what item) +;; (add-association destination-topic what item))))) +;; +;;(defmethod initialize-instance :around ((instance TopicC) &key (psis nil) (locators nil) (reified nil)) +;; "implement the pseudo-initargs :topic-ids, :persistent-ids, and :subject-locators" +;; (declare (list psis)) +;; (declare (list locators)) +;; (call-next-method) +;; ;item-identifiers are handled in the around-method for ReifiableConstructs, +;; ;TopicIdentificationCs are handled in make-construct of TopicC +;; (dolist (persistent-id psis) +;; (declare (PersistentIdC persistent-id)) +;; (setf (identified-construct persistent-id) instance)) +;; (dolist (subject-locator locators) +;; (declare (SubjectLocatorC subject-locator)) +;; (setf (identified-construct subject-locator) instance)) +;; (when reified +;; (setf (reified instance) reified))) +;; +;; +;;(defmethod delete-construct :before ((construct TopicC)) +;; (dolist (dependent (append (topic-identifiers construct) +;; (psis construct) +;; (locators construct) +;; (names construct) +;; (occurrences construct) +;; (player-in-roles construct) +;; (used-as-type construct))) +;; (delete-construct dependent)) +;; (dolist (theme (used-as-theme construct)) +;; (elephant:remove-association construct 'used-as-theme theme)) +;; (dolist (tm (in-topicmaps construct)) +;; (elephant:remove-association construct 'in-topicmaps tm)) +;; (when (reified construct) +;; (slot-makunbound (reified construct) 'reifier))) +;; +;;(defun get-all-constructs-by-uri (uri) +;; (delete +;; nil +;; (mapcar +;; (lambda (identifier) +;; (and +;; (slot-boundp identifier 'identified-construct) +;; (identified-construct identifier))) +;; (union +;; (union +;; (elephant:get-instances-by-value 'ItemIdentifierC 'uri uri) +;; (elephant:get-instances-by-value 'PersistentIdC 'uri uri)) +;; (elephant:get-instances-by-value 'SubjectLocatorC 'uri uri))))) +;; +;; +;;(defun find-existing-topic (item-identifiers locators psis) +;; (let +;; ((uris +;; (mapcar #'uri +;; (union (union item-identifiers locators) psis))) +;; (existing-topics nil)) +;; (dolist (uri uris) +;; (setf existing-topics +;; (nunion existing-topics +;; (get-all-constructs-by-uri uri) +;; :key #'internal-id))) +;; (assert (<= (length existing-topics) 1)) +;; (first existing-topics))) +;; +;; +;;(defmethod make-construct ((class-symbol (eql 'TopicC)) &rest args +;; &key start-revision item-identifiers locators psis topicid xtm-id) +;; (let +;; ((existing-topic +;; (find-existing-topic item-identifiers locators psis))) +;; (if existing-topic +;; (progn +;; ;our problem with topics is that we know only after the +;; ;addition of all the identifiers and characteristics if +;; ;anything has changed. We can't decide that here, so we must +;; ;add all revisions (real or imaginary) to version history +;; ;and decide the rest in changed-p. Maybe somebody can think +;; ;of a better way? +;; (add-to-version-history existing-topic +;; :start-revision start-revision) +;; (init-topic-identification existing-topic topicid xtm-id +;; :revision start-revision) +;; (let* ;add new identifiers to existing topics +;; ((all-new-identifiers +;; (union (union item-identifiers locators) psis)) +;; (all-existing-identifiers +;; (get-all-identifiers-of-construct existing-topic))) +;; (mapc +;; (lambda (identifier) +;; (setf (identified-construct identifier) existing-topic)) +;; (set-difference all-new-identifiers all-existing-identifiers +;; :key #'uri :test #'string=)) +;; (mapc #'delete-construct +;; (delete-if +;; (lambda (identifier) +;; (slot-boundp identifier 'identified-construct)) +;; all-new-identifiers))) +;; (check-for-duplicate-identifiers existing-topic) +;; existing-topic) +;; (progn +;; (let* +;; ((cleaned-args (remove-nil-values args)) +;; (new-topic +;; (apply #'make-instance 'TopicC cleaned-args))) +;; +;; (init-topic-identification new-topic topicid xtm-id +;; :revision start-revision) +;; (check-for-duplicate-identifiers new-topic) +;; (add-to-version-history new-topic +;; :start-revision start-revision) +;; new-topic))))) +;; +;;(defmethod make-construct :around ((class-symbol (eql 'TopicC)) +;; &key start-revision &allow-other-keys) +;; (declare (ignorable start-revision)) +;; (call-next-method)) +;; +;; +;;(defmethod equivalent-constructs ((topic1 TopicC) (topic2 TopicC)) +;; "TMDM, 5.3.5: Equality rule: Two topic items are equal if they have: +;; +;;* at least one equal string in their [subject identifiers] properties, +;; +;;* at least one equal string in their [item identifiers] properties, +;; +;;* at least one equal string in their [subject locators] properties, +;; +;;* an equal string in the [subject identifiers] property of the one +;;topic item and the [item identifiers] property of the other, or the +;;same information item in their [reified] properties (TODO: this rule +;;is currently ignored)" +;; ;(declare (optimize (debug 3))) +;; (let +;; ((psi-uris1 +;; (map 'list #'uri (psis topic1))) +;; (psi-uris2 +;; (map 'list #'uri (psis topic2))) +;; (ii-uris1 +;; (map 'list #'uri (item-identifiers topic1))) +;; (ii-uris2 +;; (map 'list #'uri (item-identifiers topic2))) +;; (locators1 +;; (map 'list #'uri (locators topic1))) +;; (locators2 +;; (map 'list #'uri (locators topic2)))) +;; (let +;; ((all-uris1 +;; (union psi-uris1 (union ii-uris1 locators1) :test #'string=)) +;; (all-uris2 +;; (union psi-uris2 (union ii-uris2 locators2) :test #'string=))) +;; ;;TODO: consider what we should do about this. If the topic at a +;; ;;given revision doesn't exist yet, it correctly has no uris +;; ;;(for that version) +;; ;; (when (= 0 (length all-uris1)) +;;;; (error (make-condition 'no-identifier-error :message "Topic1 has no identifier" :internal-id (internal-id topic1)))) +;;;; (when (= 0 (length all-uris2)) +;;;; (error (make-condition 'no-identifier-error :message "Topic2 has no identifier" :internal-id (internal-id topic2)))) +;; (intersection +;; all-uris1 all-uris2 +;; :test #'string=)))) +;; +;;(defmethod get-all-identifiers-of-construct ((top TopicC)) +;; (append (psis top) +;; (locators top) +;; (item-identifiers top))) +;; +;; +;;(defmethod topicid ((top TopicC) &optional (xtm-id nil)) +;; "Return the primary id of this item (= essentially the OID). If +;;xtm-id is explicitly given, return one of the topicids in that +;;TM (which must then exist)" +;; (if xtm-id +;; (let +;; ((possible-identifications +;; (remove-if-not +;; (lambda (top-id) +;; (string= (xtm-id top-id) xtm-id)) +;; (elephant:get-instances-by-value +;; 'TopicIdentificationC +;; 'identified-construct +;; top)))) +;; (unless possible-identifications +;; (error (make-condition +;; 'object-not-found-error +;; :message +;; (format nil "Could not find an object ~a in xtm-id ~a" top xtm-id)))) +;; (uri (first possible-identifications))) +;; (format nil "t~a" +;; (internal-id top)))) +;; +;; +;;(defgeneric psis-p (top) +;; (:documentation "Test for the existence of PSIs") +;; (:method ((top TopicC)) (slot-predicate top 'psis))) +;; +;;(defgeneric list-instanceOf (topic &key tm) +;; (:documentation "Generate a list of all topics that this topic is an +;; instance of, optionally filtered by a topic map")) +;; +;;(defmethod list-instanceOf ((topic TopicC) &key (tm nil)) +;; (remove-if +;; #'null +;; (map 'list #'(lambda(x) +;; (when (loop for psi in (psis (instance-of x)) +;; when (string= (uri psi) "http://psi.topicmaps.org/iso13250/model/instance") +;; return t) +;; (loop for role in (roles (parent x)) +;; when (not (eq role x)) +;; return (player role)))) +;; (if tm +;; (remove-if-not +;; (lambda (role) +;; ;(format t "player: ~a" (player role)) +;; ;(format t "parent: ~a" (parent role)) +;; ;(format t "topic: ~a~&" topic) +;; (in-topicmap tm (parent role))) +;; (player-in-roles topic)) +;; (player-in-roles topic))))) +;; +;; +;;(defgeneric list-super-types (topic &key tm) +;; (:documentation "Generate a list of all topics that this topic is an +;; subclass of, optionally filtered by a topic map")) +;; +;; +;;(defmethod list-super-types ((topic TopicC) &key (tm nil)) +;; (remove-if +;; #'null +;; (map 'list #'(lambda(x) +;; (when (loop for psi in (psis (instance-of x)) +;; when (string= (uri psi) *subtype-psi*) +;; return t) +;; (loop for role in (roles (parent x)) +;; when (not (eq role x)) +;; return (player role)))) +;; (if tm +;; (remove-if-not +;; (lambda (role) +;; (format t "player: ~a" (player role)) +;; (format t "parent: ~a" (parent role)) +;; (format t "topic: ~a~&" topic) +;; (in-topicmap tm (parent role))) +;; (player-in-roles topic)) +;; (player-in-roles topic))))) +;; +;; +;;(defun string-starts-with (str prefix) +;; "Checks if string str starts with a given prefix" +;; (declare (string str prefix)) +;; (string= str prefix :start1 0 :end1 +;; (min (length prefix) +;; (length str)))) +;; +;; +;;(defun get-item-by-item-identifier (uri &key revision) +;; "get a construct by its item identifier. Returns nil if the item does not exist in a +;;particular revision" +;; (declare (string uri)) +;; (declare (integer revision)) +;; (let +;; ((ii-obj +;; (elephant:get-instance-by-value 'ItemIdentifierC +;; 'uri uri))) +;; (when ii-obj +;; (find-item-by-revision +;; (identified-construct ii-obj) revision)))) +;; +;; +;;(defun get-item-by-psi (psi &key (revision 0)) +;; "get a topic by its PSI. Returns nil if the item does not exist in a +;;particular revision" +;; (declare (string psi)) +;; (declare (integer revision)) +;; (let +;; ((psi-obj +;; (elephant:get-instance-by-value 'PersistentIdC +;; 'uri psi))) +;; (when psi-obj +;; (find-item-by-revision +;; (identified-construct psi-obj) revision)))) +;; +;;(defun get-item-by-id (topicid &key (xtm-id *current-xtm*) (revision 0) (error-if-nil nil)) +;; "get a topic by its id, assuming a xtm-id. If xtm-id is empty, the current TM +;;is chosen. If xtm-id is nil, choose the global TM with its internal ID, if +;;applicable in the correct revision. If revison is provided, then the code checks +;;if the topic already existed in this revision and returns nil otherwise. +;;If no item meeting the constraints was found, then the return value is either +;;NIL or an error is thrown, depending on error-if-nil." +;; (declare (integer revision)) +;; (let +;; ((result +;; (if xtm-id +;; (let +;; ((possible-items +;; (delete-if-not +;; (lambda (top-id) +;; (and +;; (string= (xtm-id top-id) xtm-id) +;; (string= (uri top-id) topicid))) ;fixes a bug in +;; ;get-instances-by-value +;; ;that does a +;; ;case-insensitive +;; ;comparision +;; (elephant:get-instances-by-value +;; 'TopicIdentificationC +;; 'uri +;; topicid)))) +;; (when (and possible-items +;; (identified-construct-p (first possible-items))) +;; (unless (= (length possible-items) 1) +;; (error (make-condition 'duplicate-identifier-error +;; :message +;; (format nil "(length possible-items ~a) for id ~a und xtm-id ~a > 1" possible-items topicid xtm-id) +;; :uri topicid))) +;; (let +;; ((found-topic +;; (identified-construct (first possible-items)))) +;; (if (= revision 0) +;; found-topic +;; (find-item-by-revision found-topic revision))))) +;; (make-instance 'TopicC :from-oid (subseq topicid 1))))) +;; (if (and error-if-nil (not result)) +;; (error (format nil "no such item (id: ~a, tm: ~a, rev: ~a)" topicid xtm-id revision)) +;; result))) +;; +;; +;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; RoleC +;; +;;(elephant:defpclass RoleC (ReifiableConstructC TypableC) +;; ((parent :accessor parent +;; :initarg :parent +;; :associate AssociationC +;; :documentation "Association that this role belongs to") +;; (player :accessor player +;; :initarg :player +;; :associate TopicC +;; :documentation "references the topic that is the player in this role")) +;; (:documentation "The role that this topic plays in an association (formerly member)")) +;; +;; +;; +;;(defgeneric RoleC-p (object) +;; (:documentation "test if object is a of type RoleC") +;; (:method ((object t)) nil) +;; (:method ((object RoleC)) object)) +;; +;; +;;(defgeneric parent-p (vi) +;; (:documentation "t if this construct has a parent construct") +;; (:method ((constr RoleC)) (slot-predicate constr 'parent))) +;; +;; +;;(defmethod delete-construct :before ((construct RoleC)) +;; ;the way we use roles, we cannot just delete the parent association +;; ;(at least the second role won't have one left then and will +;; ;complain) +;; (delete-1-n-association construct 'parent) +;; (delete-1-n-association construct 'player)) +;; +;;(defmethod find-all-equivalent ((construct RoleC)) +;; (let +;; ((parent (and (slot-boundp construct 'parent) +;; (parent construct)))) +;; (when parent +;; (delete-if-not #'(lambda (cand) (strictly-equivalent-constructs construct cand)) +;; (slot-value parent 'roles))))) +;; +;; +;;(defmethod equivalent-constructs ((role1 RoleC) (role2 RoleC)) +;; "Association role items are equal if the values of their [type], [player], and [parent] properties are equal (TMDM 5.8)" +;; ;for the purposes for which we use this method (namely the +;; ;construction of associations), roles will initially always be +;; ;unequal regarding their parent properties +;; (and +;; (= (internal-id (instance-of role1)) (internal-id (instance-of role2))) +;; (= (internal-id (player role1)) (internal-id (player role2))))) +;; +;; +;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; AssociationC +;; +;;(elephant:defpclass AssociationC (ReifiableConstructC ScopableC TypableC) +;; ((roles :accessor roles +;; :associate (RoleC parent) +;; :documentation "(non-empty) list of this association's roles") +;; (in-topicmaps +;; :associate (TopicMapC associations) +;; :many-to-many t +;; :documentation "list of all topic maps this association is part of")) +;; (:documentation "Association in a Topic Map") +;; (:index t)) +;; +;; +;;(defmethod in-topicmaps ((association AssociationC) &key (revision *TM-REVISION*)) +;; (filter-slot-value-by-revision association 'in-topicmaps :start-revision revision)) +;; +;; +;;(defgeneric AssociationC-p (object) +;; (:documentation "test if object is a of type AssociationC") +;; (:method ((object t)) nil) +;; (:method ((object AssociationC)) object)) +;; +;; +;;(defmethod initialize-instance :around ((instance AssociationC) +;; &key +;; (roles nil)) +;; "implements the pseudo-initarg :roles" +;; (declare (list roles)) +;; (let +;; ((association (call-next-method))) +;; (dolist (role-data roles) +;; (make-instance +;; 'RoleC +;; :instance-of (getf role-data :instance-of) +;; :player (getf role-data :player) +;; :item-identifiers (getf role-data :item-identifiers) +;; :reifier (getf role-data :reifier) +;; :parent association)))) +;; +;;(defmethod make-construct :around ((class-symbol (eql 'AssociationC)) +;; &key +;; start-revision +;; &allow-other-keys) +;; (declare (ignorable start-revision)) +;; (let +;; ((association +;; (call-next-method))) +;; (declare (AssociationC association)) +;; (dolist (role (slot-value association 'roles)) +;; (unless (versions role) +;; (add-to-version-history role +;; :start-revision start-revision))) +;; association)) +;; +;;(defmethod copy-item-identifiers :around +;; ((from-construct AssociationC) +;; (to-construct AssociationC)) +;; "Internal method to copy over item idenfiers from one association +;;with its roles to another one. Role identifiers are also +;;copied. Returns nil if neither association nor role identifiers had to be copied" +;; (let +;; ((item-identifiers-copied-p nil)) ;rather brutal solution. find a better one +;; (when (call-next-method) +;; (setf item-identifiers-copied-p t)) +;; (do ((from-roles (roles from-construct) (rest from-roles)) +;; (to-roles (roles to-construct) (rest to-roles))) +;; ((null from-roles) 'finished) +;; (let +;; ((from-role (first from-roles)) +;; (to-role (first to-roles))) +;; (when +;; (mapc +;; (lambda (identifier) +;; (setf (identified-construct identifier) +;; to-role)) +;; (set-difference (item-identifiers from-role) +;; (item-identifiers to-role) +;; :key #'uri :test #'string=)) +;; (setf item-identifiers-copied-p t)))) +;; item-identifiers-copied-p)) +;; +;;(defmethod delete-construct :before ((construct AssociationC)) +;; (dolist (role (roles construct)) +;; (delete-construct role)) +;; (dolist (tm (in-topicmaps construct)) +;; (elephant:remove-association construct 'in-topicmaps tm))) +;; +;;(defmethod find-all-equivalent ((construct AssociationC)) +;; (let +;; ((some-player (player (or +;; (second (roles construct)) +;; (first (roles construct)))))) ;; dirty, dirty... but brings a tenfold speedup! +;; (delete-if-not +;; #'(lambda (cand) +;; (unless (eq construct cand) +;; (equivalent-constructs construct cand))) +;; ;here we need to use the "internal" API and access the players +;; ;with slot-value (otherwise we won't be able to merge with +;; ;'deleted' associations) +;; (mapcar #'parent (slot-value some-player 'player-in-roles))))) +;; +;; +;;(defmethod equivalent-constructs ((assoc1 AssociationC) (assoc2 AssociationC)) +;; "Association items are equal if the values of their [scope], [type], and [roles] properties are equal (TMDM 5.7)" +;; (and +;; (= (internal-id (instance-of assoc1)) (internal-id (instance-of assoc2))) +;; (not (set-exclusive-or (themes assoc1) (themes assoc2) +;; :key #'internal-id)) +;; (not (set-exclusive-or +;; (roles assoc1) +;; (roles assoc2) +;; :test #'equivalent-constructs)))) +;; +;; +;;(elephant:defpclass TopicMapC (ReifiableConstructC) +;; ((topics :accessor topics +;; :associate (TopicC in-topicmaps) +;; :documentation "list of topics that explicitly belong to this TM") +;; (associations :accessor associations +;; :associate (AssociationC in-topicmaps) +;; :documentation "list of associations that belong to this TM")) +;; (:documentation "Topic Map")) +;; +;;(defmethod equivalent-constructs ((tm1 TopicMapC) (tm2 TopicMapC)) +;; "Topic Map items are equal if one of their identifiers is equal" +;; ;Note: TMDM does not make any statement to this effect, but it's the +;; ;one logical assumption +;; (intersection +;; (item-identifiers tm1) +;; (item-identifiers tm2) +;; :test #'equivalent-constructs)) +;; +;;(defmethod find-all-equivalent ((construct TopicMapC)) +;; (let +;; ((tms (elephant:get-instances-by-class 'd:TopicMapC))) +;; (delete-if-not +;; (lambda(tm) +;; (strictly-equivalent-constructs construct tm)) +;; tms))) +;; +;;(defgeneric add-to-topicmap (tm top) +;; (:documentation "add a topic or an association to a topic +;; map. Return the added construct")) +;; +;;(defmethod add-to-topicmap ((tm TopicMapC) (top TopicC)) +;; ;TODO: add logic not to add pure topic stubs unless they don't exist yet in the store +;;; (elephant:add-association tm 'topics top) ;by adding the elephant association in this order, there will be missing one site of this association +;; (elephant:add-association top 'in-topicmaps tm) +;; top) +;; +;;(defmethod add-to-topicmap ((tm TopicMapC) (ass AssociationC)) +;; ;(elephant:add-association tm 'associations ass) +;; (elephant:add-association ass 'in-topicmaps tm) +;; ass) +;; +;;(defgeneric in-topicmap (tm constr &key revision) +;; (:documentation "Is a given construct (topic or assiciation) in this topic map?")) +;; +;;(defmethod in-topicmap ((tm TopicMapC) (top TopicC) &key (revision 0)) +;; (when (find-item-by-revision top revision) +;; (find (d:internal-id top) (d:topics tm) :test #'= :key #'d:internal-id))) +;; +;; +;;(defmethod in-topicmap ((tm TopicMapC) (ass AssociationC) &key (revision 0)) +;; (when (find-item-by-revision ass revision) +;; (find (d:internal-id ass) (d:associations tm) :test #'= :key #'d:internal-id))) +;; +;;;;;;;;;;;;;;;;;;; +;;;; reification +;; +;;(defgeneric add-reifier (construct reifier-topic) +;; (:method ((construct ReifiableConstructC) reifier-topic) +;; (let ((err "From add-reifier(): ")) +;; (declare (TopicC reifier-topic)) +;; (cond +;; ((and (not (reifier construct)) +;; (not (reified reifier-topic))) +;; (setf (reifier construct) reifier-topic) +;; (setf (reified reifier-topic) construct)) +;; ((and (not (reified reifier-topic)) +;; (reifier construct)) +;; (merge-reifier-topics (reifier construct) reifier-topic)) +;; ((and (not (reifier construct)) +;; (reified reifier-topic)) +;; (error "~a~a ~a reifies already another object ~a" +;; err (psis reifier-topic) (item-identifiers reifier-topic) +;; (reified reifier-topic))) +;; (t +;; (when (not (eql (reified reifier-topic) construct)) +;; (error "~a~a ~a reifies already another object ~a" +;; err (psis reifier-topic) (item-identifiers reifier-topic) +;; (reified reifier-topic))) +;; (merge-reifier-topics (reifier construct) reifier-topic))) +;; construct))) +;; +;; +;;(defgeneric remove-reifier (construct) +;; (:method ((construct ReifiableConstructC)) +;; (let ((reifier-topic (reifier construct))) +;; (when reifier-topic +;; (elephant:remove-association construct 'reifier reifier-topic) +;; (elephant:remove-association reifier-topic 'reified construct))))) +;; +;; +;;(defgeneric merge-reifier-topics (old-topic new-topic) +;; ;;the reifier topics are not only merged but also bound to the reified-construct +;; (:method ((old-topic TopicC) (new-topic TopicC)) +;; (unless (eql old-topic new-topic) +;; ;merges all identifiers +;; (move-identifiers old-topic new-topic) +;; (move-identifiers old-topic new-topic :what 'locators) +;; (move-identifiers old-topic new-topic :what 'psis) +;; (move-identifiers old-topic new-topic :what 'topic-identifiers) +;; ;merges all typed-object-associations +;; (dolist (typed-construct (used-as-type new-topic)) +;; (remove-association typed-construct 'instance-of new-topic) +;; (add-association typed-construct 'instance-of old-topic)) +;; ;merges all scope-object-associations +;; (dolist (scoped-construct (used-as-theme new-topic)) +;; (remove-association scoped-construct 'themes new-topic) +;; (add-association scoped-construct 'themes old-topic)) +;; ;merges all topic-maps +;; (dolist (tm (in-topicmaps new-topic)) +;; (add-association tm 'topics old-topic)) ;the new-topic is removed from this tm by deleting it +;; ;merges all role-players +;; (dolist (a-role (player-in-roles new-topic)) +;; (remove-association a-role 'player new-topic) +;; (add-association a-role 'player old-topic)) +;; ;merges all names +;; (dolist (name (names new-topic)) +;; (remove-association name 'topic new-topic) +;; (add-association name 'topic old-topic)) +;; ;merges all occurrences +;; (dolist (occurrence (occurrences new-topic)) +;; (remove-association occurrence 'topic new-topic) +;; (add-association occurrence 'topic old-topic)) +;; ;merges all version-infos +;; (let ((versions-to-move +;; (loop for vrs in (versions new-topic) +;; when (not (find-if #'(lambda(x) +;; (and (= (start-revision x) (start-revision vrs)) +;; (= (end-revision x) (end-revision vrs)))) +;; (versions old-topic))) +;; collect vrs))) +;; (dolist (vrs versions-to-move) +;; (remove-association vrs 'versioned-construct new-topic) +;; (add-association vrs 'versioned-construct old-topic))) +;; (delete-construct new-topic)) +;; ;TODO: order/repair all version-infos of the topic itself and add all new +;; ; versions to the original existing objects of the topic +;; old-topic)) \ No newline at end of file
Added: branches/new-datamodel/src/unit_tests/datamodel_test.lisp ============================================================================== --- (empty file) +++ branches/new-datamodel/src/unit_tests/datamodel_test.lisp Thu Feb 11 14:21:40 2010 @@ -0,0 +1,101 @@ +;;+----------------------------------------------------------------------------- +;;+ Isidorus +;;+ (c) 2008-2009 Marc Kuester, Christoph Ludwig, Lukas Giessmann +;;+ +;;+ Isidorus is freely distributable under the LGPL license. +;;+ You can find a detailed description in trunk/docs/LGPL-LICENSE.txt. +;;+----------------------------------------------------------------------------- + + +(defpackage :datamodel-test + (:use + :common-lisp + :datamodel + :it.bese.FiveAM + :fixtures + :unittests-constants) + (:export :run-datamodel-tests + :test-VersionInfoC + :test-VersionedConstructC)) + + +(declaim (optimize (debug 3))) + +(in-package :datamodel-test) + +(def-suite datamodel-test + :description "tests various key functions of the datamodel") + +(in-suite datamodel-test) + +(defvar *db-dir* "data_base") + +(test test-VersionInfoC () + "Tests various functions of the VersionInfoC class." + (with-fixture with-empty-db (*db-dir*) + (let ((vi-1 (make-instance 'd::VersionInfoC + :start-revision 100 + :end-revision 300)) + (vi-2 (make-instance 'd::VersionInfoC + :start-revision 300)) + (vc (make-instance 'd::VersionedConstructC))) + (is (= (d::start-revision vi-1) 100)) + (is (= (d::end-revision vi-1) 300)) + (is (= (d::start-revision vi-2) 300)) + (is (= (d::end-revision vi-2) 0)) + (is-false (d::versioned-construct-p vi-1)) + (setf (d::versioned-construct vi-1) vc) + (is-true (d::versioned-construct-p vi-1))))) + + +(test test-VersionedConstructC () + "Tests various functions of the VersionedCoinstructC class." + (with-fixture with-empty-db (*db-dir*) + (let ((vc (make-instance 'd::VersionedConstructC))) + (is-false (d::versions vc)) + (d::add-to-version-history vc + :start-revision 100 + :end-revision 300) + (is (= (length (d::versions vc)) 1)) + (is (= (d::end-revision (first (d::versions vc))) 300)) + (is (= (d::start-revision (first (d::versions vc))) 100)) + (d::add-to-version-history vc :start-revision 300) + (is (= (length (d::versions vc)) 1)) + (is (= (d::end-revision (first (d::versions vc))) 0)) + (is (= (d::start-revision (first (d::versions vc))) 100)) + (d::add-to-version-history vc :start-revision 500) + (is (= (length (d::versions vc)) 2)) + (let* ((vi-1 (first (d::versions vc))) + (vi-2 (second (d::versions vc))) + (sr-1 (d::start-revision vi-1)) + (er-1 (d::end-revision vi-1)) + (sr-2 (d::start-revision vi-2)) + (er-2 (d::end-revision vi-2))) + (is-true (or (and (= sr-1 100) (= er-1 500) + (= sr-2 500) (= er-2 0)) + (and (= sr-1 500) (= er-1 0) + (= sr-2 100) (= er-2 500))))) + (d::add-to-version-history vc :start-revision 600) + (is (= (length (d::versions vc)) 3)) + (map 'list #'(lambda(vi) + (is-true (d::versioned-construct-p vi))) + (d::versions vc)) + (d::add-to-version-history vc + :start-revision 100 + :end-revision 500) + (is (= (length (d::versions vc)) 3)) + (is (= (length (elephant:get-instances-by-class 'd::VersionInfoC)) 3)) + (is (= (length + (elephant:get-instances-by-class 'd::VersionedConstructC)) 1)) + (d::delete-construct vc) + (is (= (length (elephant:get-instances-by-class 'd::VersionInfoC)) 0)) + (is (= (length + (elephant:get-instances-by-class 'd::VersionedConstructC)) 0))))) + + + + +(defun run-datamodel-tests() + (it.bese.fiveam:run! 'test-VersionInfoC) + (it.bese.fiveam:run! 'test-VersionedConstructC) +) \ No newline at end of file
Modified: branches/new-datamodel/src/unit_tests/fixtures.lisp ============================================================================== --- branches/new-datamodel/src/unit_tests/fixtures.lisp (original) +++ branches/new-datamodel/src/unit_tests/fixtures.lisp Thu Feb 11 14:21:40 2010 @@ -37,7 +37,8 @@ :*XTM-MERGE1-TM* :*XTM-MERGE2-TM* :rdf-init-db - :rdf-test-db)) + :rdf-test-db + :with-empty-db))
(in-package :fixtures)
@@ -210,4 +211,11 @@ (&body) (handler-case (delete-file exported-file-path) (error () )) ;do nothing - (tear-down-test-db))) \ No newline at end of file + (tear-down-test-db))) + + +(def-fixture with-empty-db (dir) + (clean-out-db dir) + (elephant:open-store (xml-importer:get-store-spec dir)) + (&body) + (tear-down-test-db)) \ No newline at end of file