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