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