8d43e6995ac21af847bb9fa4d7df8d7f9fd38b20
[hyperobject.git] / views.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:     views.lisp
6 ;;;; Purpose:  View methods for Hyperobjects
7 ;;;; Author:   Kevin M. Rosenberg
8 ;;;; Created:  Apr 2000
9 ;;;;
10 ;;;; $Id$
11 ;;;;
12 ;;;; This file is Copyright (c) 2000-2004 by Kevin M. Rosenberg
13 ;;;; *************************************************************************
14  
15 (in-package #:hyperobject)
16
17
18 (defclass object-view ()
19   ((object-class :initform nil :initarg :object-class
20                  :accessor object-class
21                  :documentation "Class of object to be viewed.")
22    (slots :initform nil :initarg :slots :accessor slots
23           :documentation "List of effective slots for object to be viewed.")
24    (id :initform nil :initarg :id :accessor id
25        :documentation "id for this view.")
26    (source-code :initform nil :initarg :source-code :accessor source-code 
27                 :documentation "Source code for generating view.")
28    (country-language :initform :en :initarg :country-language
29                      :documentation "Country's Language for this view.")
30    (printer :initform nil :initarg :printer :accessor printer
31             :documentation "default function that prints the object")
32    ;;
33    (file-start-str :type (or string null) :initform nil :initarg :file-start-str
34                    :accessor file-start-str)
35    (file-end-str :type (or string null) :initform nil :initarg :file-end-str
36                  :accessor file-end-str)
37    (list-start-printer :type (or string function null) :initform nil
38                            :initarg :list-start-printer
39                       :accessor list-start-printer)
40    (list-start-indent :initform nil :initarg :list-start-indent
41                       :accessor list-start-indent)
42    (list-end-printer :type (or string function null) :initform nil
43                          :initarg :list-end-printer
44                     :accessor list-end-printer)
45    (list-end-indent :initform nil :initarg :list-end-indent
46                     :accessor list-end-indent)
47    (obj-start-printer :type (or string function null) :initform nil :initarg :obj-start-printer
48                      :accessor obj-start-printer)
49    (obj-start-indent :initform nil :initarg :obj-start-indent
50                      :accessor obj-start-indent)
51    (obj-end-printer :type (or string function null) :initform nil :initarg :obj-end-printer
52                    :accessor obj-end-printer)
53    (obj-end-indent :initform nil :initarg :obj-end-indent
54                    :accessor obj-end-indent)
55    (subobj-start-printer :type (or string function null) :initform nil :initarg :subobj-start-printer
56                      :accessor subobj-start-printer)
57    (subobj-start-indent :initform nil :initarg :subobj-start-indent
58                      :accessor subobj-start-indent)
59    (subobj-end-printer :type (or string function null) :initform nil :initarg :subobj-end-printer
60                    :accessor subobj-end-printer)
61    (subobj-end-indent :initform nil :initarg :subobj-end-indent
62                    :accessor subobj-end-indent)
63    (obj-data-indent :initform nil :initarg :obj-data-indent
64                     :accessor obj-data-indent)
65    (obj-data-printer :type (or function null) :initform nil
66                         :initarg :obj-data-printer
67                         :accessor obj-data-printer)
68    (obj-data-print-code :type (or function null) :initform nil
69                   :initarg :obj-data-print-code
70                   :accessor obj-data-print-code)
71    (obj-data-start-printer :type (or function string null) :initform nil
72                      :initarg :obj-data-start-printer
73                      :accessor obj-data-start-printer)
74    (obj-data-end-printer :type (or string null) :initform nil
75                         :initarg :obj-data-end-printer
76                         :accessor obj-data-end-printer)
77    (indenter :type (or function null) :initform nil
78              :accessor indenter
79              :documentation "Function that performs hierarchical indenting")
80    (link-slots :type list :initform nil
81                :documentation "List of slot names that have hyperlinks"
82                :accessor link-slots)
83    (link-page :type (or string null) :initform nil
84                       :initarg :link-page
85                       :accessor link-page)
86    (link-href-start :type (or string null) :initform nil :initarg :link-href-start
87                     :accessor link-href-start)
88    (link-href-end :type (or string null) :initform nil :initarg :link-href-end
89                   :accessor link-href-end)
90    (link-ampersand :type (or string null) :initform nil :initarg :link-ampersand
91                    :accessor link-ampersand))
92   (:default-initargs :link-page "meta-search.html")
93   (:documentation "View class for a hyperobject"))
94
95 (defun get-default-view-id (obj-cl)
96   (aif (views obj-cl)
97       (id (car it))
98       :compact-text))
99
100 (defun find-view-id-in-class-precedence (obj-cl vid)
101   "Looks for a view in class and parent classes"
102   (when (typep obj-cl 'hyperobject-class)
103     (aif (find vid (views obj-cl) :key #'id :test #'eq)
104          it
105          (let (cpl)
106            (handler-case
107                (setq cpl (class-precedence-list obj-cl))
108              (error (e)
109                (declare (ignore e))
110                ;; can't get cpl unless class finalized
111                (make-instance (class-name obj-cl))
112                (setq cpl (class-precedence-list obj-cl))))
113            (find-view-id-in-class-precedence (second cpl) vid)))))
114                                             
115   
116 (defun get-view-id (obj vid &optional slots)
117   "Find or make a category view for an object"
118   (let ((obj-cl (class-of obj)))
119     (unless vid
120       (setq vid (get-default-view-id obj-cl)))
121     (aif (find-view-id-in-class-precedence obj-cl vid)
122          it
123          (let ((view
124                 (make-instance 'object-view
125                   :object-class (class-name obj-cl)
126                   :id vid
127                   :slots slots)))
128            (push view (views obj-cl))
129            view))))
130
131 ;;;; *************************************************************************
132 ;;;;  Metaclass Intialization
133 ;;;; *************************************************************************
134
135 (defun finalize-views (cl)
136   "Finalize all views that are given on a objects initialization"
137   (unless (default-print-slots cl)
138     (setf (default-print-slots cl)
139           (mapcar #'slot-definition-name (class-slots cl))))
140   (setf (views cl)
141     (loop for view-def in (direct-views cl)
142         collect (make-object-view cl view-def))))
143
144 (defun make-object-view (cl view-def)
145   "Make an object view from a definition. Do nothing if a class is passed so that reinitialization will be a no-op"
146   (cond
147     ((typep view-def 'object-view)
148      view-def)
149     ((eq view-def :default)
150      (make-instance 'object-view 
151        :object-class (class-name cl)
152        :id :compact-text))
153     ((consp view-def)
154      (make-instance 'object-view
155                     :object-class (class-name cl)
156                     :id (getf view-def :id)
157                     :slots (getf view-def :slots)
158                     :source-code (getf view-def :source-code)))
159     (t
160      (error "Invalid parameter to make-object-view: ~S" view-def))))
161
162 (defmethod initialize-instance :after ((self object-view)
163                                        &rest initargs 
164                                        &key
165                                        &allow-other-keys)
166   (initialize-view self))
167   
168 (defun initialize-view (view)
169   "Calculate all view slots for a hyperobject class"
170   (let ((obj-cl (find-class (object-class view))))
171     (cond
172      ((source-code view)
173       (initialize-view-by-source-code view))
174      ((id view)
175       (initialize-view-by-id obj-cl view))
176      (t
177       (setf (id view) :compact-text)
178       (initialize-view-by-id obj-cl view)))))
179
180
181
182 (defun initialize-view-by-source-code (view)
183   "Initialize a view based upon a source code"
184   (let* ((source-code (source-code view))
185          (printer `(lambda
186                        (,(intern (symbol-name '#:self) 
187                                  (symbol-package (object-class view)))
188                         ,(intern (symbol-name '#:s) 
189                                  (symbol-package (object-class view))))
190                      (declare (ignorable 
191                                ,(intern (symbol-name '#:self) 
192                                         (symbol-package (object-class view)))
193                                ,(intern (symbol-name '#:s) 
194                                         (symbol-package (object-class view)))))
195                      (with-slots ,(slots view) 
196                          ,(intern (symbol-name '#:self) 
197                                   (symbol-package (object-class view)))
198                        ,@source-code))))
199     (setf (printer view) 
200       (compile nil (eval printer)))))
201
202 (defmacro write-simple (v s)
203   `(typecase ,v
204     (string
205      (write-string ,v ,s))
206     (fixnum
207      (write-fixnum ,v ,s))
208     (symbol
209      (write-string (symbol-name ,v) ,s))
210     (t
211      (write-string (write-to-string ,v) ,s))))
212
213 (defun write-ho-value (obj name type formatter cdata strm)
214   (declare (ignorable type))
215   (let* ((slot-data (slot-value obj name))
216          (fmt-data (if formatter
217                        (funcall formatter slot-data)
218                        slot-data)))
219     (if cdata
220         (write-cdata fmt-data strm)
221         (write-simple fmt-data strm))))
222
223 (defun ppfc-html (title name type formatter cdata print-func)
224   (vector-push-extend '(write-string "<span class=\"" s) print-func)
225   (vector-push-extend `(write-string ,title s) print-func)
226   (vector-push-extend '(write-string "\">" s) print-func)
227   (vector-push-extend `(write-ho-value x ',name ',type ',formatter ,cdata s) print-func)
228   (vector-push-extend '(write-string "</span>" s) print-func))
229
230 (defun ppfc-xml (tag name type formatter cdata print-func)
231   (vector-push-extend '(write-char #\< s) print-func)
232   (vector-push-extend `(write-string ,tag s) print-func)
233   (vector-push-extend '(write-char #\> s) print-func)
234   (vector-push-extend `(write-ho-value x ',name ',type ',formatter ,cdata s) print-func)
235   (vector-push-extend '(write-string "</" s) print-func)
236   (vector-push-extend `(write-string ,tag s) print-func)
237   (vector-push-extend '(write-char #\> s) print-func))
238
239 (defun ppfc-display-table (title name type formatter cdata print-func)
240   (vector-push-extend '(write-string "<td>" s) print-func)
241   (ppfc-html title name type formatter cdata print-func)
242   (vector-push-extend '(write-string "</td>" s) print-func))
243                       
244 (defun ppfc-html-labels (label name type formatter cdata print-func)
245   (vector-push-extend '(write-string "<span class=\"label\">" s) print-func)
246   (vector-push-extend `(write-string ,label s) print-func)
247   (vector-push-extend '(write-string "</span> " s) print-func)
248   (ppfc-html label name type formatter cdata print-func))
249
250 (defun ppfc-xhtml-labels (label tag name type formatter cdata print-func)
251   (vector-push-extend '(write-string "<span class=\"label\">" s) print-func)
252   (vector-push-extend `(write-string ,label s) print-func)
253   (vector-push-extend '(write-string "</span> " s) print-func)
254   (ppfc-html tag name type formatter cdata print-func))
255
256 (defun ppfc-xml-labels (label tag name type formatter cdata print-func)
257   (vector-push-extend '(write-string "<label>" s) print-func)
258   (vector-push-extend `(write-string ,label s) print-func)
259   (vector-push-extend '(write-string "</label> " s) print-func)
260   (ppfc-xml tag name type formatter cdata print-func))
261
262 (defun ppfc-html-link (name type formatter cdata nlink print-func)
263   (declare (fixnum nlink))
264   (vector-push-extend '(write-char #\< s) print-func)
265   (vector-push-extend `(write-string (nth ,(+ nlink nlink) links) s) print-func) 
266   (vector-push-extend '(write-char #\> s) print-func)
267   (vector-push-extend `(write-ho-value x ',name ',type ',formatter ,cdata s) print-func)
268   (vector-push-extend '(write-string "</" s) print-func)
269   (vector-push-extend `(write-string (nth ,(+ nlink nlink 1) links) s) print-func) 
270   (vector-push-extend '(write-char #\> s) print-func))
271
272 (defun ppfc-html-link-labels (label name type formatter cdata nlink print-func)
273   (vector-push-extend '(write-string "<span class=\"label\">" s) print-func)
274   (vector-push-extend `(write-string ,label s) print-func)
275   (vector-push-extend '(write-string "</span> " s) print-func)
276   (ppfc-html-link name type formatter cdata nlink print-func))
277
278 (defun push-print-fun-code (vid slot nlink print-func)
279   (let* ((formatter (esd-print-formatter slot))
280          (name (slot-definition-name slot))
281          (user-name (esd-user-name slot))
282          (xml-user-name (escape-xml-string user-name))
283          (xml-tag (escape-xml-string user-name))
284          (type (slot-value slot 'type))
285          (cdata (not (null
286                       (and (in vid :xml :xhtml :xml-link :xhtml-link
287                                :xml-labels :ie-xml-labels
288                                :xhtml-link-labels :xml-link-labels :ie-xml-link
289                                :ie-xml-link-labels)
290                            (or formatter
291                                (lisp-type-is-a-string type))))))
292          (hyperlink (esd-hyperlink slot)))
293     
294     (case vid
295       (:compact-text
296        (vector-push-extend
297         `(write-ho-value x ',name ',type ',formatter ,cdata s) print-func))
298       (:compact-text-labels
299        (vector-push-extend `(write-string ,user-name s) print-func)
300        (vector-push-extend '(write-char #\space s) print-func)
301        (vector-push-extend
302         `(write-ho-value x ',name ',type ',formatter ,cdata s) print-func))
303       ((or :html :xhtml)
304        (ppfc-html user-name name type formatter cdata print-func))
305       (:xml
306        (ppfc-xml xml-tag name type formatter cdata print-func))
307       (:html-labels
308        (ppfc-html-labels user-name name type formatter cdata print-func))
309       (:xhtml-labels
310        (ppfc-xhtml-labels xml-user-name user-name name type formatter cdata print-func))
311       ((:display-table :display-table-labels)
312        (ppfc-display-table user-name name type formatter cdata print-func))
313       (:xml-labels
314        (ppfc-xml-labels xml-user-name xml-tag name type formatter cdata print-func))
315       ((or :html-link :xhtml-link)
316        (if hyperlink
317            (ppfc-html-link name type formatter cdata nlink print-func)
318            (ppfc-html user-name name type formatter cdata print-func)))
319       ((or :xml-link :ie-xml-link)
320        (if hyperlink
321            (ppfc-html-link name type formatter cdata nlink print-func)
322            (ppfc-xml xml-tag name type formatter cdata print-func)))
323       (:html-link-labels
324        (if hyperlink
325            (ppfc-html-link-labels user-name name type formatter cdata nlink
326                                   print-func)
327            (ppfc-html-labels user-name name type formatter cdata print-func)))
328       (:xhtml-link-labels
329        (if hyperlink
330            (ppfc-html-link-labels xml-user-name name type formatter cdata nlink
331                                   print-func)
332            (ppfc-xhtml-labels xml-tag user-name name type formatter cdata
333                               print-func)))
334       ((or :xml-link-labels :ie-xml-link-labels)
335        (if hyperlink
336            (ppfc-html-link-labels xml-user-name name type formatter cdata nlink
337                                   print-func)
338            (ppfc-xml-labels xml-tag user-name name type formatter cdata
339                             print-func))))))
340
341
342 (defun view-has-links-p (view)
343   (in (id view) :html-link :xhtml-link :xml-link :ie-xml-link
344       :html-link-labels :xhtml-link-labels :xml-link-labels
345       :ie-xml-link-labels))
346
347 (defun creatable-view-id-p (obj-cl vid)
348   "Returns T if a view id can be created for this class"
349   (declare (ignore obj-cl))
350   (in vid :compact-text :compact-text-labels
351       :html :html-labels :html-link-labels
352       :xhtml :xhtml-labels :xhtml-link-labels
353       :xhtml-link :html-link
354       :xml :xml-labels :xml-link :ie-xml-link
355       :xml-link-labels :ie-xml-link-labels
356       :display-table :display-table-labels :edit-table :edit-table-labels))
357
358 (defun initialize-view-by-id (obj-cl view)
359   "Initialize a view based upon a preset vid"
360   (unless (creatable-view-id-p obj-cl (id view))
361     (error "Unable to automatically create view id ~A" (id view)))
362   
363   (unless (slots view) (setf (slots view) (default-print-slots obj-cl)))
364
365   (let ((links '())
366         (print-func (make-array 20 :fill-pointer 0 :adjustable t)))
367
368     (do* ((slots (slots view) (cdr slots))
369           (slot-name (car slots) (car slots))
370           (slot (find-slot-by-name obj-cl slot-name)
371                 (find-slot-by-name obj-cl slot-name)))
372          ((null slots))
373       (unless slot
374         (error "Slot ~A is not found in class ~S" slot-name obj-cl))
375       
376       (push-print-fun-code (id view) slot (length links) print-func)
377       (when (> (length slots) 1)
378         (vector-push-extend '(write-char #\space s) print-func))
379
380       (when (and (view-has-links-p view) (esd-hyperlink slot))
381         (push (slot-definition-name slot) links)))
382
383     (vector-push-extend 'x print-func) ;; return object
384     (setf (obj-data-print-code view) `(lambda (x s links)
385                                        (declare (ignorable s links))
386                                        ,@(map 'list #'identity print-func)))
387     (setf (obj-data-printer view)
388           (compile nil (eval (obj-data-print-code view))))
389     
390     (setf (link-slots view) (nreverse links)))
391
392   (finalize-view-by-id view)
393   view)
394
395 (defun finalize-view-by-id (view)
396   (case (id view)
397     ((or :compact-text :compact-text-labels)
398      (initialize-text-view view))
399     ((or :html :html-labels)
400      (initialize-html-view view))
401     ((or :xhtml :xhtml-labels)
402      (initialize-xhtml-view view))
403     ((or :xml :xml-labels)
404      (initialize-xml-view view))
405     ((or :html-link :html-link-labels)
406      (initialize-html-view view)
407      (setf (link-href-start view) "a href=")
408      (setf (link-href-end view) "a")
409      (setf (link-ampersand view) "&"))
410     ((or :xhtml-link :xhtml-link-labels)
411      (initialize-xhtml-view view)
412      (setf (link-href-start view) "a href=")
413      (setf (link-href-end view) "a")
414      (setf (link-ampersand view) "&amp;"))
415     ((or :display-table :display-table-labels :edit-tables)
416      (initialize-table-view view)
417      (when (in (id view) :display-table-labels :edit-table-labels)
418        (setf (list-start-printer view) #'table-label-list-start-func))
419      (setf (link-href-start view) "a href=")
420      (setf (link-href-end view) "a")
421      (setf (link-ampersand view) "&amp;"))
422     ((or :xml-link :xml-link-labels)
423      (initialize-xml-view view)
424      (setf (link-href-start view)
425            "xmllink xlink:type=\"simple\" xlink:href=")
426      (setf (link-href-end view) "xmllink")
427      (setf (link-ampersand view) "&amp;"))
428     ((or :ie-xml-link :ie-xml-link-labels)
429      (initialize-xml-view view)
430      (setf (link-href-start view) "html:a href=")
431      (setf (link-href-end view) "html:a")
432      (setf (link-ampersand view) "&amp;"))))
433
434
435 ;;;; *************************************************************************
436 ;;;;  View Data Format Section
437 ;;;; *************************************************************************
438
439 (defun class-name-of (obj)
440   (string-downcase (class-name (class-of obj))))
441
442 (defvar +newline-string+ (format nil "~%"))
443
444 (defun write-user-name-maybe-plural (obj nitems strm)
445   (write-string
446    (if (> nitems 1)
447        (hyperobject-class-user-name-plural obj)
448        (hyperobject-class-user-name obj))
449    strm))
450
451 (defun initialize-text-view (view)
452   (setf (list-start-printer view)
453         (compile nil
454                  (eval '(lambda (obj nitems indent strm)
455                          (declare (ignore indent))
456                          (write-user-name-maybe-plural obj nitems strm)
457                          (write-char #\: strm)
458                          (write-char #\Newline strm)))))
459   (setf (list-start-indent view) t)
460   (setf (obj-data-indent view) t)
461   (setf (obj-data-end-printer view) +newline-string+)
462   (setf (indenter view) #'indent-spaces))
463
464 (defun html-list-start-func (obj nitems indent strm)
465   (write-string "<div class=\"ho-username\" style=\"margin-left:" strm)
466   (write-fixnum (+ indent indent) strm)
467   (write-string "em;\">" strm)
468   (write-user-name-maybe-plural obj nitems strm)
469   (write-string "</div>" strm)
470   (write-char #\newline strm)
471   (write-string "<ul>" strm)
472   (write-char #\newline strm))
473
474 (defun initialize-html-view (view)
475   (initialize-text-view view)
476   (setf (indenter view) #'indent-spaces)
477   (setf (file-start-str view) (format nil "<html><body>~%"))
478   (setf (file-end-str view) (format nil "</body><html>~%"))
479   (setf (list-start-indent view) t)
480   (setf (list-start-printer view) #'html-list-start-func)
481   (setf (list-end-printer view) (format nil "</ul>~%"))
482   (setf (list-end-indent view) t)
483   (setf (obj-start-indent view) nil)
484   (setf (obj-start-printer view) "<li>")
485   (setf (obj-end-indent view)  nil)
486   (setf (obj-end-printer view)  (format nil "</li>~%"))
487   (setf (obj-data-end-printer view) nil)
488   (setf (obj-data-indent view) nil))
489
490 (defun xhtml-list-start-func (obj nitems indent strm)
491   (write-string "<div class=\"ho-username\" style=\"margin-left:" strm)
492   (write-fixnum (+ indent indent) strm)
493   (write-string "em;\">" strm)
494   (write-user-name-maybe-plural obj nitems strm)
495   (write-string "</div>" strm)
496   (write-string "<div style=\"margin-left:" strm)
497   (write-fixnum (+ indent indent) strm)
498   (write-string "em;\">" strm)
499   (write-char #\newline strm))
500
501 (defun table-list-start-func (obj nitems indent strm)
502   (write-string "<div class=\"ho-username\" style=\"margin-left:" strm)
503   (write-fixnum (+ indent indent) strm)
504   (write-string "em;\">" strm)
505   (write-user-name-maybe-plural obj nitems strm)
506   (write-string "</div>" strm)
507   (write-char #\newline strm)
508   (write-string "<table style=\"margin-left:" strm)
509   (write-fixnum (+ indent indent) strm)
510   (write-string "em;\">" strm)
511   (write-string "<tbody>" strm)
512   (write-char #\newline strm))
513
514 (defun table-label-list-start-func (obj nitems indent strm)
515   (write-string "<div class=\"ho-username\" style=\"margin-left:" strm)
516   (write-fixnum (+ indent indent) strm)
517   (write-string "em;\">" strm)
518   (write-user-name-maybe-plural obj nitems strm)
519   (write-string "</div>" strm)
520   (write-char #\newline strm)
521   (write-string "<table style=\"margin-left:" strm)
522   (write-fixnum (+ indent indent) strm)
523   (write-string "em;\">" strm)
524   (write-string "<thead>" strm)
525   (dolist (slot (default-print-slots (class-of obj)))
526     (write-string "<th>" strm)
527     (write-string (write-to-string slot) strm)
528     (write-string "</th>" strm))
529   (write-string "</thead>" strm)
530   (write-char #\newline strm)
531   (write-string "<tbody>" strm)
532   (write-char #\newline strm))
533
534 (defun html-obj-start (obj indent strm)
535   (declare (ignore obj indent))
536   (write-string "<div style=\"margin-left:2em;\">" strm))
537
538 (defun initialize-xhtml-view (view)
539   (initialize-text-view view)
540   (setf (indenter view) #'indent-spaces)
541   (setf (file-start-str view) (format nil "<html><body>~%"))
542   (setf (file-end-str view) (format nil "</body><html>~%"))
543   (setf (list-start-indent view) nil)
544   (setf (list-start-printer view) #'xhtml-list-start-func)
545   (setf (list-end-printer view) (format nil "</div>~%"))
546   (setf (list-end-indent view) nil)
547   (setf (obj-start-indent view) nil)
548   (setf (obj-start-printer view) #'html-obj-start)
549   (setf (obj-end-printer view) (format nil "</div>~%"))
550   (setf (obj-data-indent view) nil))
551
552 (defun initialize-table-view (view)
553   (initialize-text-view view)
554   (setf (indenter view) #'indent-spaces)
555   (setf (file-start-str view) (format nil "<html><body>~%"))
556   (setf (file-end-str view) (format nil "</body><html>~%"))
557   (setf (list-start-indent view) nil)
558   (setf (list-start-printer view) #'table-list-start-func)
559   (setf (list-end-printer view) (format nil "</tbody>~%</table>~%"))
560   (setf (list-end-indent view) nil)
561   (setf (obj-start-indent view) nil)
562   (setf (obj-start-printer view) #'html-obj-start)
563   (setf (obj-start-printer view) (format nil "<tr>"))
564   (setf (obj-end-printer view) (format nil "</tr>~%"))
565   (setf (obj-data-indent view) nil))
566
567 (defun xmlformat-list-end-func (x strm)
568   (write-string "</" strm)
569   (write-string (class-name-of x) strm)
570   (write-string "list" strm)
571   (write-string ">" strm)
572   (write-char #\newline strm))
573
574 (defun xmlformat-list-start-func (x nitems indent strm)
575   (declare (ignore indent))
576   (write-char #\< strm)
577   (write-string (class-name-of x) strm)
578   (write-string "list><title>" strm)
579   (write-user-name-maybe-plural x nitems strm)
580   (write-string ":</title>" strm)
581   (write-char #\newline strm))
582
583 (defun initialize-xml-view (view)
584   (let ((name (string-downcase (symbol-name (object-class view)))))
585     (setf (file-start-str view) "")     ; (std-xml-header)
586     (setf (list-start-indent view)  t)
587     (setf (list-start-printer view) #'xmlformat-list-start-func)
588     (setf (list-end-indent view) t)
589     (setf (list-end-printer view) #'xmlformat-list-end-func)
590     (setf (obj-start-printer view) (format nil "<~(~a~)>" name))
591     (setf (obj-start-indent view) t)
592     (setf (obj-end-printer view) (format nil "</~(~a~)>~%" name))
593     (setf (subobj-end-printer view) (format nil "</~(~a~)>~%" name))
594     (setf (subobj-end-indent view) nil)
595     (setf (obj-data-indent view) nil)))
596
597
598 ;;; File Start and Ends
599
600 (defun fmt-file-start (view strm)
601   (awhen (file-start-str view)
602          (write-string it strm)))
603
604 (defun fmt-file-end (view strm)
605   (awhen (file-end-str view)
606          (write-string it strm)))
607
608 ;;; List Start and Ends
609
610 (defun fmt-list-start (obj view strm indent num-items)
611   (when (list-start-indent view)
612     (awhen (indenter view)
613            (funcall it indent strm)))
614   (awhen (list-start-printer view)
615          (if (stringp it)
616              (write-string it strm)
617              (funcall it obj num-items indent strm))))
618
619 (defun fmt-list-end (obj view strm indent num-items)
620   (declare (ignore num-items))
621   (when (list-end-indent view)
622     (awhen (indenter view)
623            (funcall it indent strm)))
624   (awhen (list-end-printer view)
625          (if (stringp it)
626              (write-string it strm)
627              (funcall it obj strm))))
628
629 ;;; Object Start and Ends
630
631
632 (defun fmt-obj-start (obj view strm indent)
633   (when (obj-start-indent view)
634     (awhen (indenter view)
635            (funcall it indent strm)))
636   (awhen (obj-start-printer view)
637          (if (stringp it)
638              (write-string it strm)
639              (funcall it obj indent strm))))
640
641 (defun fmt-obj-end (obj view strm indent)
642   (when (obj-end-indent view)
643     (awhen (indenter view)
644            (funcall it indent strm))) 
645   (awhen (obj-end-printer view)
646          (if (stringp it)
647              (write-string it strm)
648              (funcall it obj strm))))
649
650 (defun fmt-subobj-start (obj view strm indent)
651   (when (subobj-start-indent view)
652     (awhen (indenter view)
653            (funcall it indent strm)))
654   (awhen (subobj-start-printer view)
655          (if (stringp it)
656              (write-string it strm)
657              (funcall it obj indent strm))))
658
659 (defun fmt-subobj-end (obj view strm indent)
660   (when (subobj-end-indent view)
661     (awhen (indenter view)
662            (funcall it indent strm))) 
663   (awhen (subobj-end-printer view)
664          (if (stringp it)
665              (write-string it strm)
666              (funcall it obj strm))))
667   
668 ;;; Object Data 
669
670
671 (defun make-link-start (view fieldfunc fieldvalue refvars link-printer)
672   (with-output-to-string (s)
673     (write-string (link-href-start view) s)
674     (write-char #\" s)
675     (let ((link-page (link-page view)))
676       (cond
677         ((null link-printer)
678          (write-string (make-url link-page) s)
679          (write-string "?func=" s)
680          (write-simple fieldfunc s)
681          (write-string (link-ampersand view) s)
682          (write-string "key=" s)
683          (write-simple fieldvalue s)
684          (dolist (var refvars)
685            (write-string (link-ampersand view) s)
686            (write-simple (car var) s)
687            (write-char #\= s)
688            (write-simple (cdr var) s)))
689         (link-printer
690          (funcall link-printer link-page fieldfunc fieldvalue refvars s))))
691     (write-char #\" s)))
692   
693 (defun make-link-end (obj view fieldname)
694   (declare (ignore obj fieldname))
695   (link-href-end view))
696
697 (defun fmt-obj-data (obj view strm indent refvars link-printer)
698   (awhen (obj-data-start-printer view)
699          (if (stringp it)
700              (write-string it strm)
701              (funcall it obj strm)))
702   (when (obj-data-indent view)
703     (awhen (indenter view)
704            (funcall it indent strm)))
705   (if (link-slots view)
706       (fmt-obj-data-with-link obj view strm refvars link-printer)
707       (fmt-obj-data-plain obj view strm))
708   (awhen (obj-data-end-printer view)
709          (if (stringp it)
710              (write-string it strm)
711              (funcall it obj strm))))
712
713 (defun fmt-obj-data-plain (obj view strm)
714   (awhen (obj-data-printer view)
715          (funcall it obj strm nil)))
716
717 (defun fmt-obj-data-with-link (obj view strm refvars link-printer)
718   (let ((refvalues '()))
719     (declare (dynamic-extent refvalues))
720     ;; make list of hyperlink link fields for printing to refstr template
721     (dolist (name (link-slots view))
722       (awhen (find name (hyperobject-class-hyperlinks obj) :key #'name)
723              (push (make-link-start view (lookup it) (slot-value obj name)
724                                     (append (link-parameters it) refvars)
725                                     link-printer)
726                    refvalues)
727              (push (make-link-end obj view name) refvalues)))
728     (funcall (obj-data-printer view) obj strm (nreverse refvalues))))
729
730 (defun obj-data (obj view)
731   "Returns the objects data as a string. Used by common-graphics outline function"
732   (with-output-to-string (s) (fmt-obj-data-plain obj view s)))
733
734 ;;; Display method for objects
735
736
737 (defun load-all-subobjects (objs)
738   "Load all subobjects if they have not already been loaded."
739   (dolist (obj (mklist objs))
740     (dolist (subobj (hyperobject-class-subobjects obj))
741       (awhen (slot-value obj (name-slot subobj))
742              (load-all-subobjects it))))
743   objs)
744
745 (defun view-subobjects (obj strm &optional vid (indent 0) filter
746                         subobjects refvars link-printer)
747   (when (hyperobject-class-subobjects obj)
748     (dolist (subobj (hyperobject-class-subobjects obj))
749       (aif (slot-value obj (name-slot subobj))
750            (view-hyperobject
751             it (get-view-id (car (mklist it)) vid)
752             strm vid (1+ indent) filter subobjects refvars
753             link-printer)))))
754
755           
756 (defun view-hyperobject (objs view strm &optional vid (indent 0) filter
757                          subobjects refvars link-printer)
758   "Display a single or list of hyperobject-class instances and their subobjects"
759   (let-when (objlist (mklist objs))
760     (let ((nobjs (length objlist))
761           (*print-pretty* nil)
762           (*print-circle* nil)
763           (*print-escape* nil)
764           (*print-readably* nil)
765           (*print-length* nil)
766           (*print-level* nil))
767       (fmt-list-start (car objlist) view strm indent nobjs)
768       (dolist (obj objlist)
769         (awhen (printer view)
770                (funcall it obj strm))
771         (unless (and filter (not (funcall filter obj)))
772           (fmt-obj-start obj view strm indent)
773           (fmt-obj-data obj view strm (1+ indent) refvars link-printer)
774           (fmt-obj-end obj view strm indent)
775           (if subobjects 
776               (progn
777                 (fmt-subobj-start obj view strm indent)
778                 (view-subobjects obj strm vid indent filter subobjects
779                                  refvars link-printer)
780                 (fmt-subobj-end obj view strm indent))
781             (fmt-subobj-start obj view strm indent))))
782       (fmt-list-end (car objlist) view strm indent nobjs)))
783   objs)
784
785
786 (defun view (objs &key (stream *standard-output*) vid view
787              filter subobjects refvars file-wrapper link-printer)
788   "EXPORTED Function: prints hyperobject-class objects. Calls view-hyperobject"
789   (let-when (objlist (mklist objs))
790     (unless view
791       (setq view (get-view-id (car objlist) vid)))
792     (when file-wrapper
793       (fmt-file-start view stream))
794     (view-hyperobject objlist view stream vid 0 filter subobjects refvars
795                       link-printer)
796     (when file-wrapper
797       (fmt-file-end view stream)))
798   objs)
799
800
801 ;;; Misc formatting
802
803 (defun fmt-comma-integer (i)
804   (format nil "~:d" i))