root/trunk/bknr/datastore/src/xml-impex/xml-class.lisp

Revision 2438, 8.9 kB (checked in by hhubner, 1 year ago)

Fix templater to work with current CXML.
Began porting lisp-ecoop over to the new framework.

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 (in-package :bknr.impex)
2
3 (defclass xml-class (indexed-class)
4   ((element :initarg :element :initform nil :accessor xml-class-element)
5    (unique-id-slot   :initarg :unique-id-slot :initform nil
6                      :documentation "if set to a slot name, this
7 signals that the slot can be used as a unique id to refer to an
8 instance of the object in a n XML update operation")
9    (unique-id-reader :initarg :unique-id-reader :initform nil
10                      :documentation "if set to a function, this
11 signals that the function can be used as a unique index-reader when
12 used in XML update operations.")))
13
14 (defmethod xml-class-unique-id-slot ((class xml-class))
15   (first (slot-value class 'unique-id-slot)))
16
17 (defmethod xml-class-unique-id-reader ((class xml-class))
18   (eval (first (slot-value class 'unique-id-reader))))
19
20 (defmethod validate-superclass ((sub xml-class) (super indexed-class))
21   t)
22
23 (defun princ-to-string-1 (object)
24   (when object
25     (princ-to-string object)))
26
27 (defclass xml-direct-slot-definition (bknr.indices::index-direct-slot-definition)
28   ((attribute :initarg :attribute
29               :initform nil
30               :documentation "Name of attribute to use to impex the slot.")
31    (element :initarg :element
32             :initform nil
33             :documentation "Name of the element to use to impex the slot.")
34    (body :initarg :body
35          :initform nil
36          :documentation "Whether the value of the slot has to be stored in the body of the class element.")
37    (containment :initarg :containment
38                 :initform nil
39                 :documentation "Containment specification for this slot, either nil, :* or :+")
40    (parser :initarg :parser
41            :initform #'identity
42            :documentation "Function used to parse the slot value from the XML string.")
43    (serializer :initarg :serializer
44                :initform #'princ-to-string-1
45                :documentation "Function used to serialize the slot back to XML.")
46
47    (object-id-slot :initarg :object-id-slot
48                    :initform nil
49                    :documentation "If this slot is non-nil, the slot's
50 value is considered to be the unique object id of the object.  During
51 export, objects which have an object-id-slot will only be serialized
52 once.  Further occurances of the same object will be referenced
53 through the object-id-slot (either an element or an attribute)")
54
55    (id-to-object :initarg :id-to-object
56                  :initform nil
57                  :documentation "Function used to get the value pointed to by the ID.")
58    (object-to-id :initarg :object-to-id
59                  :initform nil
60                  :documentation "Function used to get the ID of the object stored in the slot.")
61
62    (parent :initarg :parent
63            :initform nil
64            :documentation "Slot is a pointer to the parent object.")))
65
66 (defclass xml-effective-slot-definition (bknr.indices::index-effective-slot-definition)
67   ((body :initform nil)
68    (element :initform nil :reader xml-effective-slot-definition-element)
69    (attribute :initform nil :reader xml-effective-slot-definition-attribute)
70
71    (parser :initform nil :reader xml-effective-slot-definition-parser)
72    (serializer :initform nil :reader xml-effective-slot-definition-serializer)
73
74    (object-id-slot :initform nil :reader xml-effective-slot-definition-object-id-slot)
75    
76    (id-to-object :initform nil)
77    (object-to-id :initform nil)
78    (parent :initform nil)
79
80    (containment :initform nil :reader xml-effective-slot-definition-containment)
81    (required-p :initform nil :reader xml-effective-slot-definition-required-p)))
82
83 (defmethod print-object ((slot xml-effective-slot-definition) stream)
84   (print-unreadable-object (slot stream :type t :identity t)
85     (with-slots (attribute element body parent) slot
86       (format stream "~A (~A~@[ ~S~])" (slot-definition-name slot)
87               (cond (attribute "ATTRIBUTE")
88                     (element "ELEMENT")
89                     (body "BODY")
90                     (parent "PARENT")
91                     (t "UNKNOWN"))
92               (or attribute element)))))
93
94 (defmethod xml-class-attribute-slots ((class xml-class))
95   (remove-if #'(lambda (slot)
96                  (or (not (typep slot 'xml-effective-slot-definition))
97                      (not (slot-value slot 'attribute)))) (class-slots class)))
98
99 (defmethod xml-class-element-slots ((class xml-class))
100   (remove-if #'(lambda (slot)
101                  (or (not (typep slot 'xml-effective-slot-definition))
102                      (not (slot-value slot 'element)))) (class-slots class)))
103
104 (defmethod xml-class-body-slot ((class xml-class))
105   (let ((body-slots
106          (remove-if #'(lambda (slot)
107                         (or (not (typep slot 'xml-effective-slot-definition))
108                             (not (slot-value slot 'body)))) (class-slots class))))
109     (when (> (length body-slots) 1)
110       (error "Class ~A has more than one body slot: ~A." class body-slots))
111     (first body-slots)))
112
113 (defmethod xml-class-find-attribute-slot ((class xml-class) attribute)
114   (find attribute (xml-class-attribute-slots class)
115         :test #'string-equal
116         :key #'(lambda (slot) (slot-value slot 'attribute))))
117
118 (defmethod xml-class-find-element-slot ((class xml-class) element)
119   (find element (xml-class-element-slots class)
120         :test #'string-equal
121         :key #'(lambda (slot) (slot-value slot 'element))))
122
123 (defmethod xml-class-parent-slot ((class xml-class))
124   (let ((parent-slots
125          (remove-if #'(lambda (slot)
126                         (or (not (typep slot 'xml-effective-slot-definition))
127                             (not (slot-value slot 'parent))))
128                     (class-slots class))))
129     (when (> (length parent-slots) 1)
130       (error "Class ~A has more than one parent slot: ~A." class parent-slots))
131     (first parent-slots)))
132
133 (defmethod initialize-instance :after ((class xml-class) &key element &allow-other-keys)
134   (setf (xml-class-element class) (or (first element) (string-downcase (class-name class))))
135   (xml-class-finalize class))
136
137 (defmethod reinitialize-instance :after ((class xml-class) &key element &allow-other-keys)
138   (setf (xml-class-element class) (or (first element) (string-downcase (class-name class))))
139   (xml-class-finalize class))
140
141 (defmethod xml-class-finalize ((class xml-class))
142   (unless (class-finalized-p class)
143     (finalize-inheritance class))
144
145   (class-slots class))
146
147 (defmethod direct-slot-definition-class ((class xml-class) &key parent attribute element body &allow-other-keys)
148   (if (or attribute element body parent)
149       'xml-direct-slot-definition
150       (call-next-method)))
151
152 (defmethod effective-slot-definition-class ((class xml-class) &rest initargs)
153   (declare (ignore initargs))
154   'xml-effective-slot-definition)
155
156 (defmethod compute-effective-slot-definition :around ((class xml-class) name direct-slots)
157   (let* ((xml-directs (remove-if-not #'(lambda (class) (typep class 'xml-direct-slot-definition))
158                                      direct-slots))
159          (xml-direct (first xml-directs)))
160
161     ;; Commented out this check because I could not determine what it does and it warned me.
162     #+(or)
163     (when (> (length xml-directs) 1)
164       (dolist (slot-def (class-slots (class-of (first xml-directs))))
165         (unless (apply #'equal (mapcar #'(lambda (slot) (slot-value slot (slot-definition-name slot-def))) xml-directs))
166           (warn "Possibly conflicting slot options for overloaded slot ~A." (slot-definition-name slot-def)))))
167
168     (let ((normal-slot (call-next-method)))
169       (when (and xml-direct
170                  (typep normal-slot 'xml-effective-slot-definition))
171         (with-slots (attribute element body parent) xml-direct
172           (when (> (length (remove nil (list parent element attribute body))) 1)
173             (error "Only one of ELEMENT, ATTRIBUTE, PARENT or BODY is possible for a slot definition."))
174           (unless (or body parent)
175             (unless (or element attribute)
176               (setf element (string-downcase name)))
177             (when element
178               (setf element (if (eq t element) (string-downcase name) element)))
179             (when attribute
180               (setf attribute (if (eq t attribute) (string-downcase name) attribute)))
181             (unless (or element attribute)
182               (error "Could not find element or attribute for slot ~A." name))))
183
184         ;; copy direct-slot-definition slots to effective-slot-definition
185         (dolist (slot '(parser serializer body id-to-object object-to-id
186                         parent attribute element containment))
187           (setf (slot-value normal-slot slot)
188                 (slot-value xml-direct slot))))
189
190       (dolist (slot '(parser serializer object-id-slot object-to-id id-to-object) normal-slot)
191         (let ((value (slot-value normal-slot slot)))
192           (when value
193             (setf (slot-value normal-slot slot)
194                   (eval value)))))
195        
196       normal-slot)))
197
198 (defmethod xml-object-check-validity (object)
199   (let ((class (class-of object)))
200     (unless (typep class 'xml-class)
201       (error "Object ~a is not of metaclass XML-CLASS." object))
202     (dolist (slot (class-slots class))
203       (when (typep slot 'xml-effective-slot-definition)
204         (when (and (xml-effective-slot-definition-required-p slot)
205                    (not (slot-boundp object (slot-definition-name slot))))
206           (error "Required slot ~A is not bound in ~a."
207                  (slot-definition-name slot) object))
208         (let ((containment (xml-effective-slot-definition-containment slot)))
209           (when (and containment
210                      (eql containment :+)
211                      (null (slot-value object (slot-definition-name slot))))
212             (error "Slot ~a with containment :+ has no value."
213                    (slot-definition-name slot))))))))
Note: See TracBrowser for help on using the browser.