r3079: *** empty log message ***
[kmrcl.git] / ml-class.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-class.lisp,v 1.20 2002/10/17 00:25:05 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)))
26
27 (defun ml-class-of (obj)
28   #-(or cmu sbcl scl) (class-of obj)
29   #+sbcl (sb-pcl:class-of obj)
30   #+(or cmu scl) (pcl:class-of obj))
31
32 (defun ml-class-name (obj)
33   #-(or cmu sbcl scl) (class-name obj)
34   #+sbcl (sb-pcl:class-name obj)
35   #+(or cmu scl) (pcl:class-name obj))
36
37 (defclass ml-class (#-(or cmu sbcl) standard-class
38                       #+(or cmu scl) pcl::standard-class
39                       #+sbcl sb-pcl::standard-class)
40   ((title :initarg :title :type string :reader ml-std-title
41           :documentation 
42 "Print Title for class")
43    (fields :initarg :fields :reader ml-std-fields
44            :documentation
45 "List of field lists for printing. Format is 
46    ((fieldname type optional-formatter) ... )")
47    (subobjects-lists 
48     :initarg :subobjects-lists :reader ml-std-subobjects-lists
49     :documentation 
50 "List of fields that contain a list of subobjects objects.")
51    (ref-fields 
52     :initarg :ref-fields :type list :reader ml-std-ref-field
53     :documentation 
54     "List of fields that can be referred to by browsers. 
55 Format is ((field-name field-lookup-func other-link-params) ...)")
56    
57    ;;; The remainder of these fields are calculated one time
58    ;;; in finalize-inheritence.
59    (value-func :initform nil :type function :reader ml-std-value-func)
60    (xmlvalue-func :initform nil :type function :reader ml-std-xmlvalue-func)
61    (fmtstr-text :initform nil :type string :reader ml-std-fmtstr-text)
62    (fmtstr-html :initform nil :type string :reader ml-std-fmtstr-html)
63    (fmtstr-xml :initform nil :type string :reader ml-std-fmtstr-xml)
64    (fmtstr-text-labels :initform nil :type string :reader ml-std-fmtstr-text-labels)
65    (fmtstr-html-labels :initform nil :type string :reader ml-std-fmtstr-html-labels)
66    (fmtstr-xml-labels :initform nil :type string :reader ml-std-fmtstr-xml-labels)
67    (fmtstr-html-ref :initform nil :type string :reader ml-std-fmtstr-html-ref)
68    (fmtstr-xml-ref :initform nil :type string :reader ml-std-fmtstr-xml-ref)
69    (fmtstr-html-ref-labels :initform nil :type string :reader ml-std-fmtstr-html-ref-labels)
70    (fmtstr-xml-ref-labels :initform nil :type string :reader ml-std-fmtstr-xml-ref-labels)
71    )
72   (:default-initargs :title nil :fields nil :subobjects-lists nil :ref-fields nil)
73   (:documentation "Metaclass for Markup Language classes."))
74
75 #+(or cmu scl)
76 (defmethod pcl:finalize-inheritance :after ((cl ml-class))
77   (init-ml-class cl))
78
79
80 #+sbcl
81 (defmethod sb-pcl:finalize-inheritance :after ((cl ml-class))
82   (init-ml-class cl))
83
84
85 #+(or cmu slc)
86 (defmethod pcl:validate-superclass ((class ml-class) (superclass pcl::standard-class))
87   t)
88
89 #+sbcl
90 (defmethod sb-pcl:validate-superclass ((class ml-class) (superclass sb-pcl::standard-class))
91   t)
92
93 #+allegro
94 (defmethod mop:finalize-inheritance :after ((cl ml-class))
95   (init-ml-class cl))
96
97 #+lispworks
98 (defmethod clos:finalize-inheritance :after ((cl ml-class))
99   (init-ml-class cl))
100
101 #+lispworks
102 (defmethod clos:process-a-class-option ((class ml-class)
103                                         (name (eql :title))
104                                         value)
105   (unless value
106     (error "ml-class title must have a value"))
107   (if (null (cdr value))
108       (list name (car value))
109     (list name `',value)))
110
111 #+lispworks
112 (defmethod clos:process-a-class-option ((class ml-class)
113                                         (name (eql :fields))
114                                         value)
115   (unless value
116     (error "ml-class fields must have a value"))
117   (list name `',value))
118
119 #+lispworks
120 (defmethod clos:process-a-class-option ((class ml-class)
121                                         (name (eql :ref-fields))
122                                         value)
123   (unless value
124     (error "ml-class ref-fields must have a value"))
125   (list name `',value))
126
127 #+lispworks
128 (defmethod clos:process-a-class-option ((class ml-class)
129                                         (name (eql :subobjects-lists))
130                                         value)
131   (unless value
132     (error "ml-class subobjects-lists must have a value"))
133   (list name `',value))
134
135 ;;;; Class initialization function
136       
137 (defun init-ml-class (cl)
138     (let ((fmtstr-text "")
139           (fmtstr-html "")
140           (fmtstr-xml "")
141           (fmtstr-text-labels "")
142           (fmtstr-html-labels "")
143           (fmtstr-xml-labels "")
144           (fmtstr-html-ref "")
145           (fmtstr-xml-ref "")
146           (fmtstr-html-ref-labels "")
147           (fmtstr-xml-ref-labels "")
148           (first-field t)
149           (value-func '())
150           (xmlvalue-func '())
151           (classname (class-name cl))
152           (package (symbol-package (ml-class-name cl)))
153           (ref-fields (slot-value cl 'ref-fields)))
154       (declare (ignore classname))
155       (dolist (f (slot-value cl 'fields))
156         (let ((name (car f))
157               (namestr (symbol-name (car f)))
158               (namestr-lower (string-downcase (symbol-name (car f))))
159               (type (cadr f))
160               (formatter (caddr f))
161               (value-fmt "~a")
162               (plain-value-func nil)
163               html-str xml-str html-label-str xml-label-str)
164           
165           (when (or (eql type :integer) (eql type :fixnum))
166             (setq value-fmt "~d"))
167           
168           (when (eql type :commainteger)
169             (setq value-fmt "~:d"))
170           
171           (when (eql type :boolean)
172             (setq value-fmt "~a"))
173             
174           (if first-field
175               (setq first-field nil)
176             (progn
177               (string-append fmtstr-text " ")
178               (string-append fmtstr-html " ")
179               (string-append fmtstr-xml " ")
180               (string-append fmtstr-text-labels " ")
181               (string-append fmtstr-html-labels " ")
182               (string-append fmtstr-xml-labels " ")
183               (string-append fmtstr-html-ref " ")
184               (string-append fmtstr-xml-ref " ")
185               (string-append fmtstr-html-ref-labels " ")
186               (string-append fmtstr-xml-ref-labels " ")))
187           
188           (setq html-str (concatenate 'string "<span class=\"" namestr-lower "\">" value-fmt "</span>"))
189           (setq xml-str (concatenate 'string "<" namestr-lower ">" value-fmt "</" namestr-lower ">"))
190           (setq html-label-str (concatenate 'string "<span class=\"label\">" namestr-lower "</span> <span class=\"" namestr-lower "\">" value-fmt "</span>"))
191           (setq xml-label-str (concatenate 'string "<label>" namestr-lower "</label> <" namestr-lower ">" value-fmt "</" namestr-lower ">"))
192           
193           (string-append fmtstr-text value-fmt)
194           (string-append fmtstr-html html-str)
195           (string-append fmtstr-xml xml-str)
196           (string-append fmtstr-text-labels namestr-lower " " value-fmt)
197           (string-append fmtstr-html-labels html-label-str)
198           (string-append fmtstr-xml-labels xml-label-str)
199           
200           (if (find name ref-fields :key #'car)
201             (progn
202               (string-append fmtstr-html-ref "<~~a>" value-fmt "</~~a>")
203               (string-append fmtstr-xml-ref "<~~a>" value-fmt "</~~a>")
204               (string-append fmtstr-html-ref-labels "<span class=\"label\">" namestr-lower "</span> <~~a>" value-fmt "</~~a>")
205               (string-append fmtstr-xml-ref-labels "<label>" namestr-lower "</label> <~~a>" value-fmt "</~~a>"))
206             (progn
207               (string-append fmtstr-html-ref html-str)
208               (string-append fmtstr-xml-ref xml-str)
209               (string-append fmtstr-html-ref-labels html-label-str)
210               (string-append fmtstr-xml-ref-labels xml-label-str)))
211           
212           (if formatter
213               (setq plain-value-func 
214                 (list `(,formatter (,(intern namestr package) x))))
215             (setq plain-value-func 
216               (list `(,(intern namestr package) x))))
217           (setq value-func (append value-func plain-value-func))
218           
219           (if (eql type :cdata)
220               (setq xmlvalue-func (append xmlvalue-func (list `(xml-cdata ,@plain-value-func))))
221             (setq xmlvalue-func (append xmlvalue-func plain-value-func)))
222           ))
223       
224       (if value-func
225           (setq value-func `(lambda (x) (values ,@value-func)))
226         (setq value-func `(lambda () (values))))
227       (setq value-func (compile nil (eval value-func)))
228       
229       (if xmlvalue-func
230           (setq xmlvalue-func `(lambda (x) (values ,@xmlvalue-func)))
231         (setq xmlvalue-func `(lambda () (values))))
232       (setq xmlvalue-func (compile nil (eval xmlvalue-func)))
233
234       (setf (slot-value cl 'fmtstr-text) fmtstr-text)
235       (setf (slot-value cl 'fmtstr-html) fmtstr-html)
236       (setf (slot-value cl 'fmtstr-xml) fmtstr-xml)
237       (setf (slot-value cl 'fmtstr-text-labels) fmtstr-text-labels)
238       (setf (slot-value cl 'fmtstr-html-labels) fmtstr-html-labels)
239       (setf (slot-value cl 'fmtstr-xml-labels) fmtstr-xml-labels)
240       (setf (slot-value cl 'fmtstr-html-ref) fmtstr-html-ref)
241       (setf (slot-value cl 'fmtstr-xml-ref) fmtstr-xml-ref)
242       (setf (slot-value cl 'fmtstr-html-ref-labels) fmtstr-html-ref-labels)
243       (setf (slot-value cl 'fmtstr-xml-ref-labels) fmtstr-xml-ref-labels)
244       (setf (slot-value cl 'value-func) value-func)
245       (setf (slot-value cl 'xmlvalue-func) xmlvalue-func))
246     (values))
247
248
249 (defun ml-class-fmtstr-text (obj)
250   (slot-value (ml-class-of obj) 'fmtstr-text))
251
252 (defun ml-class-fmtstr-html (obj)
253   (slot-value (ml-class-of obj) 'fmtstr-html))
254
255 (defun ml-class-fmtstr-xml (obj)
256   (slot-value (ml-class-of obj) 'fmtstr-xml))
257
258 (defun ml-class-fmtstr-text-labels (obj)
259   (slot-value (ml-class-of obj) 'fmtstr-text-labels))
260
261 (defun ml-class-fmtstr-html-labels (obj)
262   (slot-value (ml-class-of obj) 'fmtstr-html-labels))
263
264 (defun ml-class-fmtstr-xml-labels (obj)
265   (slot-value (ml-class-of obj) 'fmtstr-xml-labels))
266
267 (defun ml-class-value-func (obj)
268   (slot-value (ml-class-of obj) 'value-func))
269
270 (defun ml-class-xmlvalue-func (obj)
271   (slot-value (ml-class-of obj) 'xmlvalue-func))
272
273 (eval-when (:compile-toplevel :load-toplevel :execute)
274 (defun ml-class-title (obj)
275   (awhen (slot-value (ml-class-of obj) 'title)
276             (if (consp it)
277                 (car it)
278               it))))
279
280 (defun ml-class-subobjects-lists (obj)
281   (slot-value (ml-class-of obj) 'subobjects-lists))
282
283 (defun ml-class-ref-fields (obj)
284   (slot-value (ml-class-of obj) 'ref-fields))
285
286 (defun ml-class-fields (obj)
287   (slot-value (ml-class-of obj) 'fields))
288
289 (defun ml-class-fmtstr-html-ref (obj)
290   (slot-value (ml-class-of obj) 'fmtstr-html-ref))
291
292 (defun ml-class-fmtstr-xml-ref (obj)
293   (slot-value (ml-class-of obj) 'fmtstr-xml-ref))
294
295 (defun ml-class-fmtstr-html-ref-labels (obj)
296   (slot-value (ml-class-of obj) 'fmtstr-html-ref-labels))
297
298 (defun ml-class-fmtstr-xml-ref-labels (obj)
299   (slot-value (ml-class-of obj) 'fmtstr-xml-ref-labels))
300
301 ;;; Class name functions
302
303 (defgeneric ml-class-stdname (x))
304 (defmethod ml-class-stdname ((name string))
305   (string-downcase (subseq name 1)))
306   
307 (defmethod ml-class-stdname ((cl standard-object))
308   (string-downcase (subseq (ml-class-name (ml-class-of cl)) 1)))
309   
310 ;;;; Generic Print functions
311
312 (defparameter *default-textformat* nil)
313 (defparameter *default-htmlformat* nil)
314 (defparameter *default-htmlrefformat* nil)
315 (defparameter *default-xhtmlformat* nil)
316 (defparameter *default-xhtmlrefformat* nil)
317 (defparameter *default-xmlformat* nil)
318 (defparameter *default-xmlrefformat* nil)
319 (defparameter *default-ie-xmlrefformat* nil)
320 (defparameter *default-nullformat* nil)
321 (defparameter *default-init-format?* nil)
322
323 (defun make-format-instance (fmt)
324   (unless *default-init-format?*
325     (setq *default-textformat* (make-instance 'textformat))
326     (setq *default-htmlformat* (make-instance 'htmlformat))
327     (setq *default-htmlrefformat* (make-instance 'htmlrefformat))
328     (setq *default-xhtmlformat* (make-instance 'xhtmlformat))
329     (setq *default-xhtmlrefformat* (make-instance 'xhtmlrefformat))
330     (setq *default-xmlformat* (make-instance 'xmlformat))
331     (setq *default-xmlrefformat* (make-instance 'xmlrefformat))
332     (setq *default-ie-xmlrefformat* (make-instance 'ie-xmlrefformat))
333     (setq *default-nullformat* (make-instance 'nullformat))
334     (setq *default-init-format?* t))
335   
336   (case fmt
337       (:text *default-textformat*)
338       (:html *default-htmlformat*)
339       (:htmlref *default-htmlrefformat*)
340       (:xhtml  *default-xhtmlformat*)
341       (:xhtmlref *default-xhtmlrefformat*)
342       (:xml  *default-xmlformat*)
343       (:xmlref *default-xmlrefformat*)
344       (:ie-xmlref *default-ie-xmlrefformat*)
345       (:null *default-nullformat*)
346       (otherwise *default-textformat*)))
347     
348 ;;;; Output format classes for print ml-classes
349
350 (defclass dataformat ()
351   ((file-start-str :type string :initarg :file-start-str :reader file-start-str)
352    (file-end-str :type string :initarg :file-end-str :reader file-end-str)
353    (list-start-fmtstr :type string :initarg :list-start-fmtstr :reader list-start-fmtstr)
354    (list-start-value-func :type function :initarg :list-start-value-func :reader list-start-value-func)
355    (list-start-indent :initarg :list-start-indent :reader list-start-indent)
356    (list-end-fmtstr :type string :initarg :list-end-fmtstr :reader list-end-fmtstr)
357    (list-end-value-func :type function :initarg :list-end-value-func :reader list-end-value-func)
358    (list-end-indent :initarg :list-end-indent :reader list-end-indent)
359    (obj-start-fmtstr :type string :initarg :obj-start-fmtstr :reader obj-start-fmtstr)
360    (obj-start-value-func :initarg :obj-start-value-func :reader obj-start-value-func)
361    (obj-start-indent :initarg :obj-start-indent :reader obj-start-indent)
362    (obj-end-fmtstr :type string :initarg :obj-end-fmtstr :reader obj-end-fmtstr)
363    (obj-end-value-func :initarg :obj-end-value-func :reader obj-end-value-func)
364    (obj-end-indent :initarg :obj-end-indent :reader obj-end-indent)
365    (obj-data-indent :initarg :obj-data-indent :reader obj-data-indent)
366    (obj-data-fmtstr :initarg :obj-data-fmtstr :reader  obj-data-fmtstr)
367    (obj-data-fmtstr-labels :initarg :obj-data-fmtstr-labels :reader  obj-data-fmtstr-labels)
368    (obj-data-end-fmtstr :initarg :obj-data-end-fmtstr :reader obj-data-end-fmtstr)
369    (obj-data-value-func :initarg :obj-data-value-func :reader obj-data-value-func)
370    (link-ref :initarg :link-ref :reader link-ref))
371   (:default-initargs :file-start-str nil :file-end-str nil :list-start-fmtstr nil :list-start-value-func nil
372                      :list-start-indent nil :list-end-fmtstr nil :list-end-value-func nil :list-end-indent nil
373                      :obj-start-fmtstr nil :obj-start-value-func nil :obj-start-indent nil
374                      :obj-end-fmtstr nil :obj-end-value-func nil :obj-end-indent nil
375                      :obj-data-indent nil :obj-data-fmtstr nil :obj-data-fmtstr-labels nil :obj-data-end-fmtstr nil
376                      :obj-data-value-func nil :link-ref nil)
377   (:documentation "Parent for all dataformat objects"))
378
379 (defclass binaryformat (dataformat)
380   ())
381
382 (defclass nullformat (dataformat)
383   ())
384
385 (defun text-list-start-value-func (obj nitems)
386   (values (ml-class-title obj) nitems))
387
388 (defclass textformat (dataformat) 
389   ()    
390   (:default-initargs :list-start-fmtstr "~a~P:~%"
391     :list-start-value-func #'text-list-start-value-func
392     :list-start-indent t
393     :obj-data-indent t
394     :obj-data-fmtstr #'ml-class-fmtstr-text
395     :obj-data-fmtstr-labels #'ml-class-fmtstr-text-labels
396     :obj-data-end-fmtstr "~%"
397     :obj-data-value-func #'ml-class-value-func))
398
399
400 (defun class-name-of (obj)
401   (string-downcase (ml-class-name (ml-class-of obj))))
402
403 (defun htmlformat-list-start-value-func (x nitems) 
404   (values (ml-class-title x) nitems (class-name-of x)))
405
406 (defclass htmlformat (textformat) 
407   ()
408   (:default-initargs :file-start-str "<html><body>~%"
409     :file-end-str "</body><html>~%"
410     :list-start-indent t
411     :list-start-fmtstr "<p><b>~a~p:</b></p><div class=\"~A\"><ul>~%"
412     :list-start-value-func #'htmlformat-list-start-value-func
413     :list-end-fmtstr "</ul></div>~%"
414     :list-end-indent t
415     :list-end-value-func #'identity
416     :obj-start-indent t
417     :obj-start-fmtstr "<li>"
418     :obj-start-value-func #'identity
419     :obj-end-indent  t
420     :obj-end-fmtstr  "</li>~%"
421     :obj-end-value-func #'identity
422     :obj-data-indent t
423     :obj-data-fmtstr #'ml-class-fmtstr-html-labels
424     :obj-data-fmtstr-labels #'ml-class-fmtstr-html-labels
425     :obj-data-value-func #'ml-class-value-func))
426
427 (defclass xhtmlformat (textformat) 
428   ()
429   (:default-initargs :file-start-str "<html><body>~%"
430     :file-end-str "</body><html>~%"
431     :list-start-indent t
432     :list-start-fmtstr "<p><b>~a~p:</b></p><div class=\"~A\"><ul>~%"
433     :list-start-value-func #'htmlformat-list-start-value-func
434     :list-end-fmtstr "</ul></div>~%"
435     :list-end-indent t
436     :list-end-value-func #'identity
437     :obj-start-indent t
438     :obj-start-fmtstr "<li>"
439     :obj-start-value-func #'identity
440     :obj-end-indent  t
441     :obj-end-fmtstr  "</li>~%"
442     :obj-end-value-func #'identity
443     :obj-data-indent t
444     :obj-data-fmtstr #'ml-class-fmtstr-html-labels
445     :obj-data-fmtstr-labels #'ml-class-fmtstr-html-labels
446     :obj-data-value-func #'ml-class-xmlvalue-func))
447
448
449 (defun xmlformat-list-end-value-func (x)
450   (format nil "~alist" (class-name-of x)))
451
452 (defun xmlformat-list-start-value-func (x nitems) 
453   (values (format nil "~alist" (class-name-of x)) (ml-class-title x) nitems))
454
455 (defclass xmlformat (textformat) 
456   ()
457   (:default-initargs :file-start-str "" ; (std-xml-header)
458     :list-start-indent  t
459     :list-start-fmtstr "<~a><title>~a~p:</title> ~%"
460     :list-start-value-func #'xmlformat-list-start-value-func
461     :list-end-indent  t
462     :list-end-fmtstr "</~a>~%"
463     :list-end-value-func #'xmlformat-list-end-value-func
464     :obj-start-fmtstr "<~a>"
465     :obj-start-value-func #'class-name-of
466     :obj-start-indent t
467     :obj-end-fmtstr "</~a>~%"
468     :obj-end-value-func #'class-name-of
469     :obj-end-indent nil
470     :obj-data-indent nil
471     :obj-data-fmtstr #'ml-class-fmtstr-xml
472     :obj-data-fmtstr-labels #'ml-class-fmtstr-xml-labels
473     :obj-data-value-func #'ml-class-xmlvalue-func))
474
475 (defclass link-ref ()
476   ((fmtstr :type function :initarg :fmtstr :accessor fmtstr)
477    (fmtstr-labels :type function :initarg :fmtstr-labels :accessor fmtstr-labels)
478    (page-name :type string :initarg :page-name :accessor page-name)
479    (href-head :type string :initarg :href-head :accessor href-head)
480    (href-end :type string :initarg :href-end :accessor href-end)
481    (ampersand :type string :initarg :ampersand :accessor ampersand))
482   (:default-initargs :fmtstr nil 
483     :fmtstr-labels nil 
484     :page-name "disp-func1" 
485     :href-head nil :href-end nil :ampersand nil)
486   (:documentation "Formatting for a linked reference"))
487
488 (defclass html-link-ref (link-ref)
489   ()
490   (:default-initargs :fmtstr #'ml-class-fmtstr-html-ref  
491     :fmtstr-labels #'ml-class-fmtstr-html-ref-labels
492     :href-head "a href=" 
493     :href-end "a" 
494     :ampersand "&"))
495
496 (defclass xhtml-link-ref (link-ref)
497   ()
498   (:default-initargs :fmtstr #'ml-class-fmtstr-html-ref  
499     :fmtstr-labels #'ml-class-fmtstr-html-ref-labels
500     :href-head "a href=" 
501     :href-end "a" 
502     :ampersand "&amp;"))
503
504 (defclass xml-link-ref (link-ref)
505   ()
506   (:default-initargs :fmtstr #'ml-class-fmtstr-xml-ref 
507                      :fmtstr-labels #'ml-class-fmtstr-xml-ref-labels
508                      :href-head "xmllink xlink:type=\"simple\" xlink:href=" 
509                      :href-end "xmllink" 
510                      :ampersand "&amp;")
511   (:documentation "Mozilla's and W3's idea of a link with XML"))
512
513 (defclass ie-xml-link-ref (xml-link-ref)
514   ()
515   (:default-initargs :href-head "html:a href=" 
516                      :href-end "html:a" )
517   (:documentation "Internet Explorer's idea of a link with XML"))
518
519
520 (defclass htmlrefformat (htmlformat)
521   ()
522   (:default-initargs :link-ref (make-instance 'html-link-ref)))
523
524 (defclass xhtmlrefformat (xhtmlformat)
525   ()
526   (:default-initargs :link-ref (make-instance 'xhtml-link-ref)))
527
528 (defclass xmlrefformat (xmlformat)
529   ()
530   (:default-initargs :link-ref (make-instance 'xml-link-ref)))
531
532 (defclass ie-xmlrefformat (xmlformat)
533   ()
534   (:default-initargs :link-ref (make-instance 'ie-xml-link-ref)))
535
536
537 ;;; File Start and Ends
538
539 (defgeneric fmt-file-start (fmt s))
540 (defmethod fmt-file-start ((fmt dataformat) (s stream)))
541
542 (defmethod fmt-file-start ((fmt textformat) (s stream))
543   (aif (file-start-str fmt)
544       (format s it)))
545
546 (defgeneric fmt-file-end (fmt s))
547 (defmethod fmt-file-end ((fmt textformat) (s stream))
548   (aif (file-end-str fmt)
549           (format s it)))
550
551 ;;; List Start and Ends
552
553 (defgeneric fmt-list-start (obj fmt s &optional indent num-items))
554 (defmethod fmt-list-start (x (fmt textformat) (s stream) &optional (indent 0) (num-items 1))
555   (if (list-start-indent fmt)
556       (indent-spaces indent s))
557   (aif (list-start-fmtstr fmt)
558           (apply #'format s it
559                  (multiple-value-list
560                   (funcall (list-start-value-func fmt) x num-items)))))
561
562 (defgeneric fmt-list-end (obj fmt s &optional indent num-items))
563 (defmethod fmt-list-end (x (fmt textformat) (s stream) &optional (indent 0) (num-items 1))
564   (declare (ignore num-items))
565   (if (list-end-indent fmt)
566       (indent-spaces indent s))
567   (aif (list-end-fmtstr fmt)
568           (apply #'format s it
569                  (multiple-value-list
570                   (funcall (list-end-value-func fmt) x)))))
571
572 ;;; Object Start and Ends
573
574 (defgeneric fmt-obj-start (obj fmt s &optional indent))
575 (defmethod fmt-obj-start (x (fmt textformat) (s stream) &optional (indent 0))
576   (if (obj-start-indent fmt)
577       (indent-spaces indent s))
578   (aif (obj-start-fmtstr fmt)
579           (apply #'format s it
580                  (multiple-value-list
581                   (funcall (obj-start-value-func fmt) x)))))
582
583 (defgeneric fmt-obj-end (obj fmt s &optional indent))
584 (defmethod fmt-obj-end (x (fmt textformat) (s stream) &optional (indent 0))
585   (if (obj-end-indent fmt)
586       (indent-spaces indent s))
587   (aif (obj-end-fmtstr fmt)
588           (apply #'format s it
589                  (multiple-value-list
590                   (funcall (obj-end-value-func fmt) x)))))
591   
592 ;;; Object Data 
593
594 (defgeneric make-link-start (obj ref fieldname fieldfunc fieldvalue refvars))
595 (defmethod make-link-start (obj (ref link-ref) fieldname fieldfunc fieldvalue refvars)
596   (declare (ignore obj fieldname))
597   (format nil "~a\"~a?func=~a~akey=~a~a\"" 
598           (href-head ref) (make-url (page-name ref)) fieldfunc 
599           (ampersand ref) fieldvalue
600           (if refvars
601               (let ((varstr ""))
602                 (dolist (var refvars)
603                   (string-append varstr (format nil "~a~a=~a" 
604                                                 (ampersand ref) (car var) (cadr var))))
605                 varstr)
606             "")))
607
608 (defgeneric make-link-end (obj ref fieldname)) 
609 (defmethod make-link-end (obj (ref link-ref) fieldname)
610   (declare (ignore obj fieldname))
611   (format nil "~a" (href-end ref))
612   )
613
614 (defgeneric fmt-obj-data (obj fmt s &optional indent label refvars))
615 (defmethod fmt-obj-data (x (fmt textformat) s
616                          &optional (indent 0) (label nil) (refvars nil))
617   (if (obj-data-indent fmt)
618       (indent-spaces indent s))
619   (if (link-ref fmt)
620       (fmt-obj-data-with-ref x fmt s label refvars)
621     (fmt-obj-data-plain x fmt s label))
622   (aif (obj-data-end-fmtstr fmt)
623        (format s it)))
624
625 (defgeneric fmt-obj-data-plain (obj fmt s label))
626 (defmethod fmt-obj-data-plain (x (fmt textformat) s label)
627   (if label
628       (apply #'format s
629              (funcall (obj-data-fmtstr-labels fmt) x)
630              (multiple-value-list 
631               (funcall (funcall (obj-data-value-func fmt) x) x)))
632     (apply #'format s (funcall (obj-data-fmtstr fmt) x)
633            (multiple-value-list
634             (funcall (funcall (obj-data-value-func fmt) x) x)))))
635
636 (defgeneric fmt-obj-data-with-ref (obj fmt s label refvars))
637 (defmethod fmt-obj-data-with-ref (x (fmt textformat) s label refvars)
638   (let ((refstr (make-ref-data-str x fmt label))
639         (refvalues nil)
640         (field-values 
641          (multiple-value-list
642           (funcall (funcall (obj-data-value-func fmt) x) x))))
643     
644     ;; make list of reference link fields for printing to refstr template
645     (dolist (field (ml-class-ref-fields x))
646       (let ((link-start 
647              (make-link-start x (link-ref fmt) (car field) (cadr field)
648                               (nth (position (car field) (ml-class-fields x) :key #'car) field-values)  
649                               (append (caddr field) refvars)))
650             (link-end (make-link-end x (link-ref fmt) (car field))))
651         (push link-start refvalues)
652         (push link-end refvalues)))
653     (setq refvalues (nreverse refvalues))
654     
655     (apply #'format s refstr refvalues)))
656
657 (defgeneric obj-data (obj))
658 (defmethod obj-data (x)
659   "Returns the objects data as a string. Used by common-graphics outline function"
660   (let ((fmt (make-format-instance :text)))
661     (apply #'format nil (funcall (obj-data-fmtstr fmt) x)
662            (multiple-value-list 
663             (funcall (funcall (obj-data-value-func fmt) x) x)))))
664
665 (defgeneric make-ref-data-str (obj fmt &optional label))
666 (defmethod make-ref-data-str (x (fmt textformat) &optional (label nil))
667   "Return fmt string for that contains ~a slots for reference link start and end"
668   (unless (link-ref fmt)
669     (error "fmt does not contain a link-ref"))
670   (let ((refstr 
671          (if label
672              (apply #'format nil (funcall (fmtstr-labels (link-ref fmt)) x)
673                     (multiple-value-list
674                       (funcall (funcall (obj-data-value-func fmt) x) x)))
675            (apply #'format nil (funcall (fmtstr (link-ref fmt)) x)
676                   (multiple-value-list (funcall (funcall (obj-data-value-func fmt) x) x))))))
677     refstr))
678   
679 ;;; Display method for objects
680
681
682 (defgeneric load-all-subobjects (objs))
683 (defmethod load-all-subobjects (objs)
684   "Load all subobjects if they have not already been loaded."
685   (when objs
686     (let ((objlist (mklist objs)))
687       (dolist (obj objlist)
688         (awhen (ml-class-subobjects-lists obj)  ;; access list of functions
689           (dolist (child-obj it)   ;; for each child function
690             (awhen (funcall (car child-obj) obj)
691               (load-all-subobjects it))))))
692     objs))
693
694 (defgeneric output-ml-class (objs fmt strm &optional label english-only-function indent subobjects refvars))
695 (defmethod output-ml-class (objs (fmt dataformat) (strm stream) 
696                             &optional (label nil) (english-only-function nil)
697                                       (indent 0) (subobjects nil) (refvars nil))
698   "Display a single or list of ml-class instances and their subobjects"
699   (when objs
700     (setq objs (mklist objs))
701     (let ((nobjs (length objs)))
702       (fmt-list-start (car objs) fmt strm indent nobjs)
703       (dolist (obj objs)
704         (unless (and english-only-function
705                   (multiple-value-bind (eng term) (funcall english-only-function obj)
706                     (and term (not eng))))
707           (fmt-obj-start obj fmt strm indent)
708           (fmt-obj-data obj fmt strm (1+ indent) label refvars)
709           (if subobjects
710               (awhen (ml-class-subobjects-lists obj)  ;; access list of functions
711                         (dolist (child-obj it)   ;; for each child function
712                           (awhen (funcall (car child-obj) obj) ;; access set of child objects
713                                     (output-ml-class it fmt strm label 
714                                                      english-only-function
715                                                      (1+ indent) subobjects refvars)))))
716           (fmt-obj-end obj fmt strm indent)))
717       (fmt-list-end (car objs) fmt strm indent nobjs))
718     t))
719
720 (defun display-ml-class (objs &key (os *standard-output*) (format :text)
721                                 (label nil) (english-only-function nil) (subobjects nil)
722                                 (file-wrapper t) (refvars nil))
723   "EXPORTED Function: displays a ml-class. Simplies call to output-ml-class"
724   (let ((fmt (make-format-instance format)))
725     (if file-wrapper
726         (fmt-file-start fmt os))
727     (when objs
728       (output-ml-class objs fmt os label english-only-function 0 subobjects refvars))
729     (if file-wrapper
730         (fmt-file-end fmt os)))
731   objs)