r2991: *** empty log message ***
[kmrcl.git] / ml.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          ml-class.lisp
6 ;;;; Purpose:       Markup Language Metaclass
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; This metaclass as functions to classes to allow display
11 ;;;; in Text, HTML, and XML formats. This includes hyperlinking
12 ;;;; capability and sub-objects.
13 ;;;;
14 ;;;; $Id: ml.lisp,v 1.1 2002/10/13 17:39:50 kevin Exp $
15 ;;;;
16 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
17 ;;;;
18 ;;;; KMRCL users are granted the rights to distribute and use this software
19 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
20 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
21 ;;;; *************************************************************************
22  
23 (in-package :kmrcl)
24
25 (declaim (optimize (speed 3) (safety 1) (debug 3) (compilation-speed 0)))
26
27 ;;; Design:
28 ;;;  ml-class hold all formatting information for an object
29 ;;;
30 ;;; When a class is definited with the def-ml-class macro, a formatting
31 ;;; object named _<name>-ml-fmt_ is created. Then, when a ml-class object
32 ;;; is to be printed, the formatting object is referenced.
33
34 (defmacro def-ml-class (name (parent) field-defs &key title types linked-fields subobjects documentaton)
35   (let ((ml-fmt-def ,(ml-fmt-def name field-defs title types linked-fields subobjects))
36         (initargs (initargs-def field-defs)))
37    `(progn
38       ,ml-fmt-def
39       
40       (defclass ,name (,parent)
41         ,field-defs
42         (:default-initargs ,initargs)
43         @,(and documentation '((:documentation ,documentation))))
44   )
45
46    (def-ml-class urank (umlsclass)
47   ((rank :type fixnum :initarg :rank :reader rank)
48    (sab :type string :initarg :sab :reader sab)
49    (tty :type string :initarg :tty :reader tty)
50    (supres :type string :initarg :supres :reader supres))
51   :title "Rank"
52   :types (rank :fixnum) (sab :string) (tty :string) (supres :string))
53
54    
55 (defclass ml-class ()
56   ((title :initarg :title :type string :reader title
57           :documentation 
58 "Print Title for class")
59    (fields :initarg :fields :reader fields
60            :documentation
61 "List of field lists for printing. Format is 
62    ((fieldname type optional-formatter) ... )")
63    (subobjects-lists 
64     :initarg :subobjects-lists :reader subobjects-lists
65     :documentation 
66 "List of fields that contain a list of subobjects objects.")
67    (ref-fields 
68     :initarg :ref-fields :type list :reader ref-field
69     :documentation 
70     "List of fields that can be referred to by browsers. 
71 Format is ((field-name field-lookup-func other-link-params) ...)")
72    
73    ;;; The remainder of these fields are calculated one time
74    ;;; in finalize-inheritence.
75    (value-func :initform nil :type function :reader value-func)
76    (xmlvalue-func :initform nil :type function :reader xmlvalue-func)
77    (fmtstr-text :initform nil :type string :reader fmtstr-text)
78    (fmtstr-html :initform nil :type string :reader fmtstr-html)
79    (fmtstr-xml :initform nil :type string :reader fmtstr-xml)
80    (fmtstr-text-labels :initform nil :type string :reader fmtstr-text-labels)
81    (fmtstr-html-labels :initform nil :type string :reader fmtstr-html-labels)
82    (fmtstr-xml-labels :initform nil :type string :reader fmtstr-xml-labels)
83    (fmtstr-html-ref :initform nil :type string :reader fmtstr-html-ref)
84    (fmtstr-xml-ref :initform nil :type string :reader fmtstr-xml-ref)
85    (fmtstr-html-ref-labels :initform nil :type string :reader fmtstr-html-ref-labels)
86    (fmtstr-xml-ref-labels :initform nil :type string :reader fmtstr-xml-ref-labels)
87    )
88   (:default-initargs :title nil :fields nil :subobjects-lists nil :ref-fields nil)
89   (:documentation "Metaclass for Markup Language classes."))
90
91
92 ;;;; Class initialization function
93       
94 (defun init-ml-class-fmt (cl)
95     (let ((fmtstr-text "")
96           (fmtstr-html "")
97           (fmtstr-xml "")
98           (fmtstr-text-labels "")
99           (fmtstr-html-labels "")
100           (fmtstr-xml-labels "")
101           (fmtstr-html-ref "")
102           (fmtstr-xml-ref "")
103           (fmtstr-html-ref-labels "")
104           (fmtstr-xml-ref-labels "")
105           (first-field t)
106           (value-func '())
107           (xmlvalue-func '())
108           (classname (class-name cl))
109           (ref-fields (slot-value cl 'ref-fields)))
110       (declare (ignore classname))
111       (dolist (f (slot-value cl 'fields))
112         (let ((name (car f))
113               (namestr (symbol-name (car f)))
114               (namestr-lower (string-downcase (symbol-name (car f))))
115               (type (cadr f))
116               (formatter (caddr f))
117               (value-fmt "~a")
118               (plain-value-func nil)
119               html-str xml-str html-label-str xml-label-str)
120           
121           (when (or (eql type :integer) (eql type :fixnum))
122             (setq value-fmt "~d"))
123           
124           (when (eql type :commainteger)
125             (setq value-fmt "~:d"))
126           
127           (when (eql type :boolean)
128             (setq value-fmt "~a"))
129             
130           (if first-field
131               (setq first-field nil)
132             (progn
133               (string-append fmtstr-text " ")
134               (string-append fmtstr-html " ")
135               (string-append fmtstr-xml " ")
136               (string-append fmtstr-text-labels " ")
137               (string-append fmtstr-html-labels " ")
138               (string-append fmtstr-xml-labels " ")
139               (string-append fmtstr-html-ref " ")
140               (string-append fmtstr-xml-ref " ")
141               (string-append fmtstr-html-ref-labels " ")
142               (string-append fmtstr-xml-ref-labels " ")))
143           
144           (setq html-str value-fmt)
145           (setq xml-str (concatenate 'string "<" namestr-lower ">" value-fmt "</" namestr-lower ">"))
146           (setq html-label-str (concatenate 'string "<i>" namestr-lower "</i> " value-fmt))
147           (setq xml-label-str (concatenate 'string "<label>" namestr-lower "</label> <" namestr-lower ">" value-fmt "</" namestr-lower ">"))
148           
149           (string-append fmtstr-text value-fmt)
150           (string-append fmtstr-html html-str)
151           (string-append fmtstr-xml xml-str)
152           (string-append fmtstr-text-labels namestr-lower " " value-fmt)
153           (string-append fmtstr-html-labels html-label-str)
154           (string-append fmtstr-xml-labels xml-label-str)
155           
156           (if (find name ref-fields :key #'car)
157             (progn
158               (string-append fmtstr-html-ref "<~~a>" value-fmt "</~~a>")
159               (string-append fmtstr-xml-ref "<~~a>" value-fmt "</~~a>")
160               (string-append fmtstr-html-ref-labels "<i>" namestr-lower "</i> <~~a>" value-fmt "</~~a>")
161               (string-append fmtstr-xml-ref-labels "<label>" namestr-lower "</label> <~~a>" value-fmt "</~~a>"))
162             (progn
163               (string-append fmtstr-html-ref html-str)
164               (string-append fmtstr-xml-ref xml-str)
165               (string-append fmtstr-html-ref-labels html-label-str)
166               (string-append fmtstr-xml-ref-labels xml-label-str)))
167           
168           (if formatter
169               (setq plain-value-func 
170                 (list `(,formatter (,(concat-symbol-pkg 
171                                       :umlisp namestr) x))))
172             (setq plain-value-func 
173               (list `(,(concat-symbol-pkg 
174                         :umlisp namestr) x))))
175           (setq value-func (append value-func plain-value-func))
176           
177           (if (eql type :cdata)
178               (setq xmlvalue-func (append xmlvalue-func (list `(xml-cdata ,@plain-value-func))))
179             (setq xmlvalue-func (append xmlvalue-func plain-value-func)))
180           ))
181
182       (setq value-func `(lambda (x) (values ,@value-func)))
183       (setq value-func (compile nil (eval value-func)))
184       (setq xmlvalue-func `(lambda (x) (values ,@xmlvalue-func)))
185       (setq xmlvalue-func (compile nil (eval xmlvalue-func)))
186
187       (setf (slot-value cl 'fmtstr-text) fmtstr-text)
188       (setf (slot-value cl 'fmtstr-html) fmtstr-html)
189       (setf (slot-value cl 'fmtstr-xml) fmtstr-xml)
190       (setf (slot-value cl 'fmtstr-text-labels) fmtstr-text-labels)
191       (setf (slot-value cl 'fmtstr-html-labels) fmtstr-html-labels)
192       (setf (slot-value cl 'fmtstr-xml-labels) fmtstr-xml-labels)
193       (setf (slot-value cl 'fmtstr-html-ref) fmtstr-html-ref)
194       (setf (slot-value cl 'fmtstr-xml-ref) fmtstr-xml-ref)
195       (setf (slot-value cl 'fmtstr-html-ref-labels) fmtstr-html-ref-labels)
196       (setf (slot-value cl 'fmtstr-xml-ref-labels) fmtstr-xml-ref-labels)
197       (setf (slot-value cl 'value-func) value-func)
198       (setf (slot-value cl 'xmlvalue-func) xmlvalue-func))
199     (values))
200
201 (defun %class-of (obj)
202   #-(or cmu sbcl) (class-of obj)
203   #+sbcl (sb-pcl:class-of obj)
204   #+cmu (pcl:class-of obj))
205
206
207 (defun ml-class-fmtstr-text (obj)
208   (slot-value (%class-of obj) 'fmtstr-text))
209
210 (defun ml-class-fmtstr-html (obj)
211   (slot-value (%class-of obj) 'fmtstr-html))
212
213 (defun ml-class-fmtstr-xml (obj)
214   (slot-value (%class-of obj) 'fmtstr-xml))
215
216 (defun ml-class-fmtstr-text-labels (obj)
217   (slot-value (%class-of obj) 'fmtstr-text-labels))
218
219 (defun ml-class-fmtstr-html-labels (obj)
220   (slot-value (%class-of obj) 'fmtstr-html-labels))
221
222 (defun ml-class-fmtstr-xml-labels (obj)
223   (slot-value (%class-of obj) 'fmtstr-xml-labels))
224
225 (defun ml-class-value-func (obj)
226   (slot-value (%class-of obj) 'value-func))
227
228 (defun ml-class-xmlvalue-func (obj)
229   (slot-value (%class-of obj) 'xmlvalue-func))
230
231 (eval-when (:compile-toplevel :load-toplevel :execute)
232 (defun ml-class-title (obj)
233   (awhen (slot-value (%class-of obj) 'title)
234             (if (consp it)
235                 (car it)
236               it))))
237
238 (defun ml-class-subobjects-lists (obj)
239   (slot-value (%class-of obj) 'subobjects-lists))
240
241 (defun ml-class-ref-fields (obj)
242   (slot-value (%class-of obj) 'ref-fields))
243
244 (defun ml-class-fields (obj)
245   (slot-value (%class-of obj) 'fields))
246
247 (defun ml-class-fmtstr-html-ref (obj)
248   (slot-value (%class-of obj) 'fmtstr-html-ref))
249
250 (defun ml-class-fmtstr-xml-ref (obj)
251   (slot-value (%class-of obj) 'fmtstr-xml-ref))
252
253 (defun ml-class-fmtstr-html-ref-labels (obj)
254   (slot-value (%class-of obj) 'fmtstr-html-ref-labels))
255
256 (defun ml-class-fmtstr-xml-ref-labels (obj)
257   (slot-value (%class-of obj) 'fmtstr-xml-ref-labels))
258
259 ;;; Class name functions
260
261 (defmethod ml-class-stdname ((name string))
262   (string-downcase (subseq name :start 1)))
263   
264 (defmethod ml-class-stdname ((cl standard-object))
265   (string-downcase (subseq (class-name (%class-of cl)) :start 1)))
266   
267 ;;;; Generic Print functions
268
269 (defparameter *default-textformat* nil)
270 (defparameter *default-htmlformat* nil)
271 (defparameter *default-htmlrefformat* nil)
272 (defparameter *default-xmlformat* nil)
273 (defparameter *default-xmlrefformat* nil)
274 (defparameter *default-nullformat* nil)
275 (defparameter *default-init-format?* nil)
276
277 (defun make-format-instance (fmt)
278   (unless *default-init-format?*
279     (setq *default-textformat* (make-instance 'textformat))
280     (setq *default-htmlformat* (make-instance 'htmlformat))
281     (setq *default-htmlrefformat* (make-instance 'htmlrefformat))
282     (setq *default-xmlformat* (make-instance 'xmlformat))
283     (setq *default-xmlrefformat* (make-instance 'xmlrefformat))
284     (setq *default-nullformat* (make-instance 'nullformat))
285     (setq *default-init-format?* t))
286   
287   (case fmt
288       (:text *default-textformat*)
289       (:html *default-htmlformat*)
290       (:htmlref *default-htmlrefformat*)
291       (:xml  *default-xmlformat*)
292       (:xmlref *default-xmlrefformat*)
293       (:null *default-nullformat*)
294       (otherwise *default-textformat*)))
295     
296 ;;;; Output format classes for print ml-classes
297
298 (defclass dataformat ()
299   ((file-start-str :type string :initarg :file-start-str :reader file-start-str)
300    (file-end-str :type string :initarg :file-end-str :reader file-end-str)
301    (list-start-fmtstr :type string :initarg :list-start-fmtstr :reader list-start-fmtstr)
302    (list-start-value-func :type function :initarg :list-start-value-func :reader list-start-value-func)
303    (list-start-indent :initarg :list-start-indent :reader list-start-indent)
304    (list-end-fmtstr :type string :initarg :list-end-fmtstr :reader list-end-fmtstr)
305    (list-end-value-func :type function :initarg :list-end-value-func :reader list-end-value-func)
306    (list-end-indent :initarg :list-end-indent :reader list-end-indent)
307    (obj-start-fmtstr :type string :initarg :obj-start-fmtstr :reader obj-start-fmtstr)
308    (obj-start-value-func :initarg :obj-start-value-func :reader obj-start-value-func)
309    (obj-start-indent :initarg :obj-start-indent :reader obj-start-indent)
310    (obj-end-fmtstr :type string :initarg :obj-end-fmtstr :reader obj-end-fmtstr)
311    (obj-end-value-func :initarg :obj-end-value-func :reader obj-end-value-func)
312    (obj-end-indent :initarg :obj-end-indent :reader obj-end-indent)
313    (obj-data-indent :initarg :obj-data-indent :reader obj-data-indent)
314    (obj-data-fmtstr :initarg :obj-data-fmtstr :reader  obj-data-fmtstr)
315    (obj-data-fmtstr-labels :initarg :obj-data-fmtstr-labels :reader  obj-data-fmtstr-labels)
316    (obj-data-end-fmtstr :initarg :obj-data-end-fmtstr :reader obj-data-end-fmtstr)
317    (obj-data-value-func :initarg :obj-data-value-func :reader obj-data-value-func)
318    (link-ref :initarg :link-ref :reader link-ref))
319   (:default-initargs :file-start-str nil :file-end-str nil :list-start-fmtstr nil :list-start-value-func nil
320                      :list-start-indent nil :list-end-fmtstr nil :list-end-value-func nil :list-end-indent nil
321                      :obj-start-fmtstr nil :obj-start-value-func nil :obj-start-indent nil
322                      :obj-end-fmtstr nil :obj-end-value-func nil :obj-end-indent nil
323                      :obj-data-indent nil :obj-data-fmtstr nil :obj-data-fmtstr-labels nil :obj-data-end-fmtstr nil
324                      :obj-data-value-func nil :link-ref nil)
325   (:documentation "Parent for all dataformat objects"))
326
327 (defclass binaryformat (dataformat)
328   ())
329
330 (defclass nullformat (dataformat)
331   ())
332
333 (defun text-list-start-value-func (obj nitems)
334   (values (ml-class-title obj) nitems))
335
336 (defclass textformat (dataformat) 
337   ()    
338   (:default-initargs :list-start-fmtstr "~a~P:~%"
339     :list-start-value-func #'text-list-start-value-func
340     :list-start-indent t
341     :obj-data-indent t
342     :obj-data-fmtstr #'ml-class-fmtstr-text
343     :obj-data-fmtstr-labels #'ml-class-fmtstr-text-labels
344     :obj-data-end-fmtstr "~%"
345     :obj-data-value-func #'ml-class-value-func))
346
347 (defclass htmlformat (textformat) 
348   ()
349   (:default-initargs :file-start-str "<html><body>~%"
350     :file-end-str "</body><html>~%"
351     :list-start-indent t
352     :list-start-fmtstr "<p><b>~a~P:</b></p><ul>~%"
353     :list-start-value-func #'text-list-start-value-func
354     :list-end-fmtstr "</ul>~%"
355     :list-end-indent t
356     :list-end-value-func #'identity
357     :obj-start-indent t
358     :obj-start-fmtstr "<li>"
359     :obj-start-value-func #'identity
360     :obj-end-indent  t
361     :obj-end-fmtstr  "</li>~%"
362     :obj-end-value-func #'identity
363     :obj-data-indent t
364     :obj-data-fmtstr #'ml-class-fmtstr-html-labels
365     :obj-data-fmtstr-labels #'ml-class-fmtstr-html-labels
366     :obj-data-value-func #'ml-class-value-func))
367
368
369 (defun class-name-of (obj)
370   (string-downcase (class-name (%class-of obj))))
371
372 (defun xmlformat-list-end-value-func (x)
373   (format nil "~alist" (string-downcase (class-name (%class-of x)))))
374
375 (defun xmlformat-list-start-value-func (x nitems) 
376   (values (format nil "~alist" (string-downcase (class-name (%class-of x)))) (ml-class-title x) nitems))
377
378 (defclass xmlformat (textformat) 
379   ()
380   (:default-initargs :file-start-str "" ; (std-xml-header)
381     :list-start-indent  t
382     :list-start-fmtstr "<~a><title>~a~p:</title> ~%"
383     :list-start-value-func #'xmlformat-list-start-value-func
384     :list-end-indent  t
385     :list-end-fmtstr "</~a>~%"
386     :list-end-value-func #'xmlformat-list-end-value-func
387     :obj-start-fmtstr "<~a>"
388     :obj-start-value-func #'class-name-of
389     :obj-start-indent t
390     :obj-end-fmtstr "</~a>~%"
391     :obj-end-value-func #'class-name-of
392     :obj-end-indent nil
393     :obj-data-indent nil
394     :obj-data-fmtstr #'ml-class-fmtstr-xml
395     :obj-data-fmtstr-labels #'ml-class-fmtstr-xml-labels
396     :obj-data-value-func #'ml-class-xmlvalue-func))
397
398 (defclass link-ref ()
399   ((fmtstr :type function :initarg :fmtstr :accessor fmtstr)
400    (fmtstr-labels :type function :initarg :fmtstr-labels :accessor fmtstr-labels)
401    (page-name :type string :initarg :page-name :accessor page-name)
402    (href-head :type string :initarg :href-head :accessor href-head)
403    (href-end :type string :initarg :href-end :accessor href-end)
404    (ampersand :type string :initarg :ampersand :accessor ampersand))
405   (:default-initargs :fmtstr nil 
406     :fmtstr-labels nil 
407     :page-name "disp-func1" 
408     :href-head nil :href-end nil :ampersand nil)
409   (:documentation "Formatting for a linked reference"))
410
411 (defclass html-link-ref (link-ref)
412   ()
413   (:default-initargs :fmtstr #'ml-class-fmtstr-html-ref  
414     :fmtstr-labels #'ml-class-fmtstr-html-ref-labels
415     :href-head "a href=" 
416     :href-end "a" 
417     :ampersand "&"))
418
419 (defclass xml-link-ref (link-ref)
420   ()
421   (:default-initargs :fmtstr #'ml-class-fmtstr-xml-ref 
422     :fmtstr-labels #'ml-class-fmtstr-xml-ref-labels
423     :href-head "xmllink xlink:type=\"simple\" xlink:href=" 
424     :href-end "xmllink" 
425     :ampersand "&amp;"))
426
427
428 (defclass htmlrefformat (htmlformat)
429   ()
430   (:default-initargs :link-ref (make-instance 'html-link-ref)))
431
432 (defclass xmlrefformat (xmlformat)
433   ()
434   (:default-initargs :link-ref (make-instance 'xml-link-ref)))
435
436
437 ;;; File Start and Ends
438
439 (defmethod fmt-file-start ((fmt dataformat) (s stream)))
440
441 (defmethod fmt-file-start ((fmt textformat) (s stream))
442   (aif (file-start-str fmt)
443       (format s it)))
444
445 (defmethod fmt-file-end ((fmt textformat) (s stream))
446   (aif (file-end-str fmt)
447           (format s it)))
448
449 ;;; List Start and Ends
450
451 (defmethod fmt-list-start (x (fmt textformat) (s stream) &optional (indent 0) (num-items 1))
452   (if (list-start-indent fmt)
453       (indent-spaces indent s))
454   (aif (list-start-fmtstr fmt)
455           (apply #'format s it
456                  (multiple-value-list
457                   (funcall (list-start-value-func fmt) x num-items)))))
458
459 (defmethod fmt-list-end (x (fmt textformat) (s stream) &optional (indent 0) (num-items 1))
460   (declare (ignore num-items))
461   (if (list-end-indent fmt)
462       (indent-spaces indent s))
463   (aif (list-end-fmtstr fmt)
464           (apply #'format s it
465                  (multiple-value-list
466                   (funcall (list-end-value-func fmt) x)))))
467
468 ;;; Object Start and Ends
469
470 (defmethod fmt-obj-start (x (fmt textformat) (s stream) &optional (indent 0))
471   (if (obj-start-indent fmt)
472       (indent-spaces indent s))
473   (aif (obj-start-fmtstr fmt)
474           (apply #'format s it
475                  (multiple-value-list
476                   (funcall (obj-start-value-func fmt) x)))))
477
478 (defmethod fmt-obj-end (x (fmt textformat) (s stream) &optional (indent 0))
479   (if (obj-end-indent fmt)
480       (indent-spaces indent s))
481   (aif (obj-end-fmtstr fmt)
482           (apply #'format s it
483                  (multiple-value-list
484                   (funcall (obj-end-value-func fmt) x)))))
485   
486 ;;; Object Data 
487
488 (defmethod make-link-start (obj (ref link-ref) fieldname fieldfunc fieldvalue refvars)
489   (declare (ignore obj fieldname))
490   (format nil "~a\"~a?func=~a~akey=~a~a\"" 
491           (href-head ref) (make-url (page-name ref)) fieldfunc 
492           (ampersand ref) fieldvalue
493           (if refvars
494               (let ((varstr ""))
495                 (dolist (var refvars)
496                   (string-append varstr (format nil "~a~a=~a" 
497                                                 (ampersand ref) (car var) (cadr var))))
498                 varstr)
499             "")))
500
501 (defmethod make-link-end (obj (ref link-ref) fieldname)
502   (declare (ignore obj fieldname))
503   (format nil "~a" (href-end ref))
504   )
505
506 (defmethod fmt-obj-data (x (fmt textformat) s
507                          &optional (indent 0) (label nil) (refvars nil))
508   (if (obj-data-indent fmt)
509       (indent-spaces indent s))
510   (if (link-ref fmt)
511       (fmt-obj-data-with-ref x fmt s label refvars)
512     (fmt-obj-data-plain x fmt s label))
513   (aif (obj-data-end-fmtstr fmt)
514        (format s it)))
515
516 (defmethod fmt-obj-data-plain (x (fmt textformat) s label)
517   (if label
518       (apply #'format s
519              (funcall (obj-data-fmtstr-labels fmt) x)
520              (multiple-value-list 
521               (funcall (funcall (obj-data-value-func fmt) x) x)))
522     (apply #'format s (funcall (obj-data-fmtstr fmt) x)
523            (multiple-value-list
524             (funcall (funcall (obj-data-value-func fmt) x) x)))))
525
526 (defmethod fmt-obj-data-with-ref (x (fmt textformat) s label refvars)
527   (let ((refstr (make-ref-data-str x fmt label))
528         (refvalues nil)
529         (field-values 
530          (multiple-value-list
531           (funcall (funcall (obj-data-value-func fmt) x) x))))
532     
533     ;; make list of reference link fields for printing to refstr template
534     (dolist (field (ml-class-ref-fields x))
535       (let ((link-start 
536              (make-link-start x (link-ref fmt) (car field) (cadr field)
537                               (nth (position (car field) (ml-class-fields x) :key #'car) field-values)  
538                               (append (caddr field) refvars)))
539             (link-end (make-link-end x (link-ref fmt) (car field))))
540         (push link-start refvalues)
541         (push link-end refvalues)))
542     (setq refvalues (nreverse refvalues))
543     
544     (apply #'format s refstr refvalues)))
545   
546 (defmethod obj-data (x)
547   "Returns the objects data as a string. Used by common-graphics outline function"
548   (let ((fmt (make-format-instance :text)))
549     (apply #'format nil (funcall (obj-data-fmtstr fmt) x)
550            (multiple-value-list 
551             (funcall (funcall (obj-data-value-func fmt) x) x)))))
552
553 (defmethod make-ref-data-str (x (fmt textformat) &optional (label nil))
554   "Return fmt string for that contains ~a slots for reference link start and end"
555   (unless (link-ref fmt)
556     (error "fmt does not contain a link-ref"))
557   (let ((refstr 
558          (if label
559              (apply #'format nil (funcall (fmtstr-labels (link-ref fmt)) x)
560                     (multiple-value-list
561                       (funcall (funcall (obj-data-value-func fmt) x) x)))
562            (apply #'format nil (funcall (fmtstr (link-ref fmt)) x)
563                   (multiple-value-list (funcall (funcall (obj-data-value-func fmt) x) x))))))
564     refstr))
565   
566 ;;; Display method for objects
567
568
569 (defmethod load-all-subobjects (objs)
570   "Load all subobjects if they have not already been loaded."
571   (when objs
572     (let ((objlist (mklist objs)))
573       (dolist (obj objlist)
574         (awhen (ml-class-subobjects-lists obj)  ;; access list of functions
575           (dolist (child-obj it)   ;; for each child function
576             (awhen (funcall (car child-obj) obj)
577               (load-all-subobjects it))))))
578     objs))
579
580 (defmethod output-ml-class (objs (fmt dataformat) (strm stream) 
581                             &optional (label nil) (english-only-function nil)
582                                       (indent 0) (subobjects nil) (refvars nil))
583   "Display a single or list of ml-class instances and their subobjects"
584   (when objs
585     (setq objs (mklist objs))
586     (let ((nobjs (length objs)))
587       (fmt-list-start (car objs) fmt strm indent nobjs)
588       (dolist (obj objs)
589         (unless (and english-only-function (not (funcall english-only-function obj)))
590           (fmt-obj-start obj fmt strm indent)
591           (fmt-obj-data obj fmt strm (1+ indent) label refvars)
592           (if subobjects
593               (awhen (ml-class-subobjects-lists obj)  ;; access list of functions
594                         (dolist (child-obj it)   ;; for each child function
595                           (awhen (funcall (car child-obj) obj) ;; access set of child objects
596                                     (output-ml-class it fmt strm label 
597                                                      english-only-function
598                                                      (1+ indent) subobjects refvars)))))
599           (fmt-obj-end obj fmt strm indent)))
600       (fmt-list-end (car objs) fmt strm indent nobjs))
601     t))
602
603 (defun display-ml-class (objs &key (os *standard-output*) (format :text)
604                                 (label nil) (english-only-function nil) (subobjects nil)
605                                 (file-wrapper t) (refvars nil))
606   "EXPORTED Function: displays a ml-class. Simplies call to output-ml-class"
607   (let ((fmt (make-format-instance format)))
608     (if file-wrapper
609         (fmt-file-start fmt os))
610     (when objs
611       (output-ml-class objs fmt os label english-only-function 0 subobjects refvars))
612     (if file-wrapper
613         (fmt-file-end fmt os)))
614   objs)