minor fixes
[clinton/lisp-on-lines.git] / src / mewa.lisp
CommitLineData
13ebe12f
DC
1(declaim (optimize (speed 2) (space 3) (safety 0)))
2
5dea194e 3(in-package :lisp-on-lines)
13ebe12f 4
579597e3 5(defparameter *default-type* :ucw)
6
2b0fd9c8
DC
7(define-layered-class description ()
8 ((description-type
9 :initarg :type
10 :accessor description.type
11 :initform 'viewer
12 :special t)
13 (description-layers
14 :initarg :layers
15 :accessor description.layers
16 :initform nil
17 :special t)
18 (description-properties
19 :accessor description.properties
20 :initform nil
21 :special t)
0386c736 22 (described-object
23 :layered-accessor object
24 :initform nil
25 :special t)
ebabbd23 26 (description-default-attributes
27 :accessor default-attributes
28 :initarg :default-attributes
29 :initform nil
30 :special t)
2b0fd9c8
DC
31 (description-attributes
32 :accessor attributes
33 :initarg :attributes
34 :initform nil
35 :special t)))
579597e3 36
2b0fd9c8
DC
37(defmethod print-object ((self description) stream)
38 (print-unreadable-object (self stream :type t)
39 (with-slots (description-type) self
7553e5e8 40 (format stream "~A" description-type))))
579597e3 41
15bc66bd
DC
42;;;; * Occurences
43
44(defvar *occurence-map* (make-hash-table)
2b0fd9c8 45 "a display is generated by associating an 'occurence'
15bc66bd 46with an instance of a class. This is usually keyed off class-name,
7553e5e8 47although an arbitrary occurence could be used with an arbitrary class.")
15bc66bd
DC
48
49(define-layered-class
2b0fd9c8 50 standard-occurence (description)
ebabbd23 51 ((occurence-name :accessor name :initarg :name)
52 (attribute-map :accessor attribute-map :initform (make-hash-table)))
15bc66bd
DC
53 (:documentation
54 "an occurence holds the attributes like a class holds slot-definitions.
55Attributes are the metadata used to display, validate, and otherwise manipulate actual values stored in lisp objects."))
56
57(defun find-or-create-occurence (name)
58 "Returns the occurence associated with this name."
59 (let ((occurence (gethash name *occurence-map*)))
60 (if occurence
61 occurence
ebabbd23 62 (let ((new-occurence (make-instance 'standard-occurence :name name)))
15bc66bd
DC
63 (setf (gethash name *occurence-map*) new-occurence)
64 new-occurence))))
65
66(defun clear-occurence (occurence)
67 "removes all attributes from the occurence"
68 (setf (attribute-map occurence) (make-hash-table)))
69
70(defgeneric find-occurence (name)
bf12489a
DC
71 (:method (thing)
72 nil)
15bc66bd
DC
73 (:method ((name symbol))
74 (find-or-create-occurence name))
bf12489a 75 (:method ((instance standard-object))
15bc66bd
DC
76 (find-or-create-occurence (class-name (class-of instance)))))
77
78
2b0fd9c8
DC
79(define-layered-class
80 attribute (description)
0386c736 81 ((attribute-name :layered-accessor attribute.name
2b0fd9c8
DC
82 :initarg :name
83 :initform (gensym "ATTRIBUTE-")
84 :special t)
85 (occurence :accessor occurence :initarg :occurence :initform nil)
0386c736 86 (label :initarg :label :layered-accessor label :initform nil :special t)))
2b0fd9c8 87
15bc66bd 88;;;; * Attributes
2b0fd9c8
DC
89(defmethod print-object ((self attribute) stream)
90 (print-unreadable-object (self stream :type t)
ebabbd23 91 (with-slots (attribute-name description-type) self
92 (format stream "~A ~A" description-type attribute-name))))
15bc66bd
DC
93
94(define-layered-class
2b0fd9c8
DC
95 standard-attribute (attribute)
96 ((setter :accessor setter :initarg :setter :special t :initform nil)
97 (getter :accessor getter :initarg :getter :special t :initform nil)
ebabbd23 98 (value :accessor value :initarg :value :special t)
99 (slot-name :accessor slot-name :initarg :slot-name :special t :initform nil))
15bc66bd
DC
100 (:documentation "Attributes are used to display a part of a thing, such as a slot of an object, a text label, the car of a list, etc."))
101
6f63d3a4 102(defmacro defattribute (name supers slots &rest args)
0386c736 103 (let* (
104 (type-provided-p (second (assoc :type-name args)))
105 (type (or type-provided-p name))
2b0fd9c8
DC
106 (layer (or (second (assoc :in-layer args)) nil))
107 (properties (cdr (assoc :default-properties args)))
ebabbd23 108 (cargs (remove-if #'(lambda (key)
2b0fd9c8
DC
109 (or (eql key :type-name)
110 (eql key :default-properties)
111 (eql key :default-initargs)
112 (eql key :in-layer)))
113 args
114 :key #'car)))
115
6f63d3a4 116 `(progn
6f63d3a4 117 (define-layered-class
91f2ab7b 118 ;;;; TODO: fix the naive way of making sure s-a is a superclass
2b0fd9c8
DC
119 ;;;; Need some MOPey goodness.
120 ,name ,@ (when layer `(:in-layer ,layer)),(or supers '(standard-attribute))
121 ,(append slots (properties-as-slots properties))
122 #+ (or) ,@ (cdr cargs)
123 ,@cargs
124 (:default-initargs :properties (list ,@properties)
125 ,@ (cdr (assoc :default-initargs args))))
126
ebabbd23 127 ,(when (or
128 type-provided-p
129 (not (find-attribute-class-for-type name)))
130 `(defmethod find-attribute-class-for-type ((type (eql ',type)))
0386c736 131 ',name)))))
15bc66bd 132(define-layered-class
2b0fd9c8 133 display-attribute (attribute)
15bc66bd
DC
134 ()
135 (:documentation "Presentation Attributes are used to display objects
136using the attributes defined in an occurence. Presentation Attributes are always named using keywords."))
137
138(defun clear-attributes (name)
139 "removes all attributes from an occurance"
140 (clear-occurence (find-occurence name)))
141
6f63d3a4
DC
142(defmethod find-attribute-class-for-type (type)
143 nil)
144
ebabbd23 145(defun make-attribute (&rest args &key type &allow-other-keys)
2b0fd9c8
DC
146 (apply #'make-instance
147 (or (find-attribute-class-for-type type)
ebabbd23 148 'standard-attribute)
149 :properties args
2b0fd9c8 150 args))
6f63d3a4 151
2b0fd9c8 152(defmethod ensure-attribute ((occurence standard-occurence) &rest args &key name &allow-other-keys)
15bc66bd 153 "Creates an attribute in the given occurence"
2b0fd9c8 154 (let ((attribute (apply #'make-attribute :occurence occurence args)))
ebabbd23 155 (setf (find-attribute occurence name) attribute)))
156
157(defmethod find-attribute ((occurence null) name)
158 nil)
15bc66bd
DC
159
160(defmethod find-attribute ((occurence standard-occurence) name)
ebabbd23 161 (or (gethash name (attribute-map occurence))
162 (let* ((class (ignore-errors (find-class (name occurence))))
163 (class-direct-superclasses
164 (when class
165 (closer-mop:class-direct-superclasses
166 class))))
167 (when class-direct-superclasses
168 (let ((attribute
169 (find-attribute
170 (find-occurence (class-name
171 (car
172 class-direct-superclasses)))
173 name)))
174 attribute)))))
15bc66bd
DC
175
176(defmethod find-all-attributes ((occurence standard-occurence))
177 (loop for att being the hash-values of (attribute-map occurence)
178 collect att))
179
2b0fd9c8
DC
180(defmethod ensure-attribute (occurence-name &rest args &key name type &allow-other-keys)
181 (declare (ignore name type))
182 (apply #'ensure-attribute
15bc66bd 183 (find-occurence occurence-name)
2b0fd9c8 184 args))
15bc66bd
DC
185
186;;;; The following functions make up the public interface to the
187;;;; MEWA Attribute Occurence system.
188
189(defmethod find-all-attributes (occurence-name)
190 (find-all-attributes (find-occurence occurence-name)))
191
192(defmethod find-attribute (occurence-name attribute-name)
2b0fd9c8 193 "Return the ATTRIBUTE named by ATTRIBUTE-NAME in OCCURANCE-name"
15bc66bd
DC
194 (find-attribute (find-occurence occurence-name) attribute-name))
195
2b0fd9c8
DC
196(defmethod (setf find-attribute) ((attribute-spec list) occurence-name attribute-name)
197 "Create a new attribute in the occurence.
198ATTRIBUTE-SPEC: a list of (type name &rest initargs)"
199 (apply #'ensure-attribute occurence-name :name attribute-name :type (first attribute-spec) (rest attribute-spec)))
15bc66bd 200
ebabbd23 201(defmethod (setf find-attribute) ((attribute standard-attribute) occurence attribute-name)
202 "Create a new attribute in the occurence.
203ATTRIBUTE-SPEC: a list of (type name &rest initargs)"
204 (setf (gethash attribute-name (attribute-map occurence))
205 attribute))
206
207(defmethod (setf find-attribute) ((attribute null) occurence attribute-name)
208 "Create a new attribute in the occurence.
209ATTRIBUTE-SPEC: a list of (type name &rest initargs)"
210 (setf (gethash attribute-name (attribute-map occurence))
211 attribute))
212
2b0fd9c8
DC
213
214(defmethod find-attribute ((attribute-with-occurence attribute) attribute-name)
215 (find-attribute (occurence attribute-with-occurence) attribute-name))
579597e3 216
15bc66bd 217(defmethod set-attribute-properties ((occurence-name t) attribute properties)
2b0fd9c8
DC
218 (setf (description.properties attribute) (plist-nunion
219 properties
220 (description.properties attribute)))
221 (loop for (initarg value) on (description.properties attribute)
222 by #'cddr
223 with map = (initargs.slot-names attribute)
224 do (let ((s-n (assoc-if #'(lambda (x) (member initarg x)) map)))
225
226 (if s-n
227 (progn
228 (setf (slot-value attribute
229 (cdr s-n))
230 value))
231 (warn "Cannot find initarg ~A in attribute ~S" initarg attribute)))
232 finally (return attribute)))
233
234(defmethod set-attribute (occurence-name attribute-name attribute-spec &key (inherit t))
235 "If inherit is T, sets the properties of the attribute only, unless the type has changed.
236otherwise, (setf find-attribute)"
237 (let ((att (find-attribute occurence-name attribute-name)))
238 (if (and att inherit (or (eql (car attribute-spec)
239 (description.type att))
240 (eq (car attribute-spec) t)))
241 (set-attribute-properties occurence-name att (cdr attribute-spec))
242 (setf (find-attribute occurence-name attribute-name)
243 (cons (car attribute-spec)
244 (plist-nunion
245 (cdr attribute-spec)
246 (when att (description.properties att))))))))
fc3e754f 247
15bc66bd 248(defmethod perform-define-attributes ((occurence-name t) attributes)
fc3e754f
DC
249 (loop for attribute in attributes
250 do (destructuring-bind (name type &rest args)
251 attribute
2b0fd9c8
DC
252 (cond ((not (null type))
253 ;;set the type as well
254 (set-attribute occurence-name name (cons type args)))))))
fc3e754f 255
15bc66bd 256(defmacro define-attributes (occurence-names &body attribute-definitions)
fc3e754f 257 `(progn
15bc66bd
DC
258 ,@(loop for occurence-name in occurence-names
259 collect `(perform-define-attributes (quote ,occurence-name) (quote ,attribute-definitions)))))
260
2b0fd9c8
DC
261(defmethod find-display-attribute (occurence name)
262 (find-attribute occurence (intern (symbol-name name) "KEYWORD")))
263
264(defmethod find-description (object type)
265 (let ((occurence (find-occurence object)))
266 (or (find-display-attribute
267 occurence
268 type)
269 occurence)))
15bc66bd 270
ebabbd23 271;;"Unused???"
15bc66bd 272(defmethod setter (attribute)
6f63d3a4 273 (warn "Setting ~A in ~A" attribute *context*)
2b0fd9c8
DC
274 (let ((setter (getf (description.properties attribute) :setter))
275 (slot-name (getf (description.properties attribute) :slot-name)))
15bc66bd
DC
276 (cond (setter
277 setter)
278 (slot-name
279 #'(lambda (value object)
280 (setf (slot-value object slot-name) value)))
281 (t
282 #'(lambda (value object)
2b0fd9c8 283 (warn "Can't find anywere to set ~A in ~A using ~A" value object attribute))))))
15bc66bd 284
d5e996b3 285
6f63d3a4
DC
286(define-layered-function attribute-value (instance attribute)
287 (:documentation " Like SLOT-VALUE for instances, the base method calls GETTER."))
288
ebabbd23 289(defmethod attribute-slot-value (instance attribute)
7553e5e8 290 "Return (VALUES slot-value-or-nil existsp boundp
291
292If this attribute, in its current context, refers to a slot,
293we return slot-value-or nil either boundp or not."
ebabbd23 294 (let (existsp boundp slot-value-or-nil)
295 (cond
296 ((and (slot-boundp attribute 'slot-name) (slot-name attribute))
297 (when (slot-exists-p instance (slot-name attribute))
298 (setf existsp t)
299 (when (slot-boundp instance (slot-name attribute))
300 (setf boundp t
301 slot-value-or-nil (slot-value
302 instance
303 (slot-name attribute))))))
304 ((and (slot-exists-p instance (attribute.name attribute)))
305 (setf existsp t)
306 (when (slot-boundp instance (attribute.name attribute))
307 (setf boundp t
308 slot-value-or-nil (slot-value
309 instance
310 (attribute.name attribute))))))
311 (VALUES slot-value-or-nil existsp boundp)))
312
6f63d3a4 313(define-layered-method attribute-value (instance (attribute standard-attribute))
ebabbd23 314 "return the attribute value or NIL if it cannot be found"
315 (with-slots (getter value) attribute
316 (when (slot-boundp attribute 'value)
317 (setf getter (constantly value)))
318 (if (and (slot-boundp attribute 'getter) getter)
319 ;;;; call the getter
320 (funcall getter instance)
321 ;;;; or default to the attribute-slot-value
322 (attribute-slot-value instance attribute))))
6f63d3a4 323
a4e6154d 324(define-layered-function (setf attribute-value) (value instance attribute))
6f63d3a4 325
2b0fd9c8
DC
326(define-layered-method
327 (setf attribute-value) (value instance (attribute standard-attribute))
2b0fd9c8
DC
328 (with-slots (setter slot-name) attribute
329 (cond ((and (slot-boundp attribute 'setter) setter)
2b0fd9c8
DC
330 (funcall setter value instance))
331 ((and (slot-boundp attribute 'slot-name) slot-name)
332 (setf (slot-value instance slot-name) value))
ebabbd23 333 ((and (slot-exists-p instance (attribute.name attribute)))
2b0fd9c8
DC
334 (setf (slot-value instance (attribute.name attribute)) value))
335 (t
336 (error "Cannot set ~A in ~A" attribute instance)))))
d5e996b3
DC
337
338
a4e6154d 339
d5e996b3
DC
340;;;; ** Default Attributes
341
342
343;;;; The default mewa class contains the types use as defaults.
344;;;; maps meta-model slot-types to slot-presentation
345
346(defvar *default-attributes-class-name* 'default)
347
15bc66bd
DC
348(defmacro with-default-attributes ((occurence-name) &body body)
349 `(let ((*default-attributes-class-name* ',occurence-name))
350 ,@body))
351
d5e996b3 352(define-attributes (default)
ebabbd23 353 (boolean boolean)
354 (string string)
355 (number currency)
356 (integer integer)
357 (currency currency)
358 (clsql:generalized-boolean boolean)
359 (foreign-key foreign-key))
d5e996b3 360
15bc66bd
DC
361(defun find-presentation-attributes (occurence-name)
362 (loop for att in (find-all-attributes occurence-name)
2b0fd9c8 363 when (typep att 'display-attribute)
15bc66bd 364 collect att))
d5e996b3 365
15bc66bd
DC
366(defun attribute-to-definition (attribute)
367 (nconc (list (attribute.name attribute)
2b0fd9c8
DC
368 (description.type attribute))
369 (description.properties attribute)))
d5e996b3 370
15bc66bd
DC
371(defun find-default-presentation-attribute-definitions ()
372 (if (eql *default-attributes-class-name* 'default)
373 (mapcar #'attribute-to-definition (find-presentation-attributes 'default))
374 (remove-duplicates (mapcar #'attribute-to-definition
375 (append
376 (find-presentation-attributes 'default)
377 (find-presentation-attributes
378 *default-attributes-class-name*))))))
d5e996b3 379(defun gen-ptype (type)
15bc66bd
DC
380 (let* ((type (if (consp type) (car type) type))
381 (possible-default (find-attribute *default-attributes-class-name* type))
382 (real-default (find-attribute 'default type)))
383 (cond
384 (possible-default
2b0fd9c8 385 (description.type possible-default))
15bc66bd 386 (real-default
2b0fd9c8 387 (description.type real-default))
15bc66bd 388 (t type))))
d5e996b3
DC
389
390(defun gen-presentation-slots (instance)
391 (mapcar #'(lambda (x) (gen-pslot (cadr x)
392 (string (car x))
393 (car x)))
394 (meta-model:list-slot-types instance)))
395
34e8e2d6 396
d5e996b3
DC
397(defun gen-pslot (type label slot-name)
398 (copy-list `(,(gen-ptype type)
399 :label ,label
400 :slot-name ,slot-name)))
401
233380f7 402;; This software is Copyright (c) Drew Crampsie, 2004-2005.