hax:%want-strings-p
[closure-common.git] / hax.lisp
blobac32602dfa03dcd38b41b17ec53ac93adb31608d
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: An event API for the HTML parser, inspired by SAX
4 ;;; Created: 2007-10-14
5 ;;; Author: David Lichteblau
6 ;;; License: BSD
7 ;;; ---------------------------------------------------------------------------
8 ;;; (c) copyright 2005,2007 David Lichteblau
10 ;;; Redistribution and use in source and binary forms, with or without
11 ;;; modification, are permitted provided that the following conditions are
12 ;;; met:
13 ;;;
14 ;;; 1. Redistributions of source code must retain the above copyright
15 ;;; notice, this list of conditions and the following disclaimer.
16 ;;;
17 ;;; 2. Redistributions in binary form must reproduce the above copyright
18 ;;; notice, this list of conditions and the following disclaimer in the
19 ;;; documentation and/or other materials provided with the distribution
20 ;;;
21 ;;; THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 ;;; WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 ;;; MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ;;; IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 ;;; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 ;;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 ;;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 ;;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 ;;; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 ;;; IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 ;;; POSSIBILITY OF SUCH DAMAGE.
33 (defpackage :hax
34 (:use :common-lisp)
35 (:export #:abstract-handler
36 #:default-handler
38 #:make-attribute
39 #:standard-attribute
40 #:find-attribute
41 #:attribute-name
42 #:attribute-value
43 #:attribute-specified-p
45 #:start-document
46 #:start-element
47 #:characters
48 #:end-element
49 #:end-document
50 #:comment
52 #+rune-is-integer
53 #:%want-strings-p))
55 (in-package :hax)
58 ;;;; ATTRIBUTE
60 (defgeneric attribute-name (attribute))
61 (defgeneric attribute-value (attribute))
62 (defgeneric attribute-specified-p (attribute))
64 (defclass standard-attribute ()
65 ((name :initarg :name :accessor attribute-name)
66 (value :initarg :value :accessor attribute-value)
67 (specified-p :initarg :specified-p :accessor attribute-specified-p)))
69 (defun make-attribute (name value &optional (specified-p t))
70 (make-instance 'standard-attribute
71 :name name
72 :value value
73 :specified-p specified-p))
75 (defun %rod= (x y)
76 ;; allow rods *and* strings *and* null
77 (cond
78 ((zerop (length x)) (zerop (length y)))
79 ((zerop (length y)) nil)
80 ((stringp x) (string= x y))
81 (t (runes:rod= x y))))
83 (defun find-attribute (name attrs)
84 (find name attrs :key #'attribute-name :test #'%rod=))
87 ;;;; ABSTRACT-HANDLER and DEFAULT-HANDLER
89 (defclass abstract-handler () ())
90 (defclass default-handler (abstract-handler) ())
92 #+rune-is-integer
93 (defgeneric %want-strings-p (handler)
94 (:method ((handler null)) nil)
95 (:method ((handler abstract-handler)) t))
97 (defgeneric start-document (handler name public-id system-id)
98 (:method ((handler null) name public-id system-id)
99 (declare (ignore name public-id system-id))
100 nil)
101 (:method ((handler default-handler) name public-id system-id)
102 (declare (ignore name public-id system-id))
103 nil))
105 (defgeneric start-element (handler name attributes)
106 (:method ((handler null) name attributes)
107 (declare (ignore name attributes))
108 nil)
109 (:method ((handler default-handler) name attributes)
110 (declare (ignore name attributes))
111 nil))
113 (defgeneric characters (handler data)
114 (:method ((handler null) data)
115 (declare (ignore data))
116 nil)
117 (:method ((handler default-handler) data)
118 (declare (ignore data))
119 nil))
121 (defgeneric end-element (handler name)
122 (:method ((handler null) name)
123 (declare (ignore name))
124 nil)
125 (:method ((handler default-handler) name)
126 (declare (ignore name))
127 nil))
129 (defgeneric end-document (handler)
130 (:method ((handler null)) nil)
131 (:method ((handler default-handler)) nil))
133 (defgeneric comment (handler data)
134 (:method ((handler null) data)
135 (declare (ignore data))
136 nil)
137 (:method ((handler default-handler) data)
138 (declare (ignore data))
139 nil))
142 ;;;; documentation
144 (setf (documentation (find-package :hax) t)
145 "An event protocol for HTML serialization, this package is similar
146 to the HAX protocol defined by cxml for XML serialization.
148 (Technically, this package should have been spelled SAH, but HAX
149 sounds better.)
151 Note that Closure HTML is not a streaming parser yet. Documents
152 are always parsed in full before the first HAX event is emitted.
153 In spite of this restriction, the HAX API is useful for HTML
154 serialization and transformation purposes, and for integration
155 with SAX.
157 @begin[HAX handlers]{section}
158 @aboutclass{abstract-handler}
159 @aboutclass{default-handler}
160 @end{section}
161 @begin[The attribute protocol]{section}
162 @aboutclass{standard-attribute}
163 @aboutfun{make-attribute}
164 @aboutfun{attribute-name}
165 @aboutfun{attribute-value}
166 @aboutfun{attribute-specified-p}
167 @end{section}
168 @begin[HAX events]{section}
169 @aboutfun{start-document}
170 @aboutfun{start-element}
171 @aboutfun{end-element}
172 @aboutfun{characters}
173 @aboutfun{comment}
174 @aboutfun{end-document}
175 @end{section}")
177 (setf (documentation 'abstract-handler 'type)
178 "@short{The superclass of all HAX handlers.}
180 Direct subclasses have to implement all event methods, since
181 no default methods are defined on this class.
183 Note that it is permissible to use handlers that are not
184 instances of this class in some circumstances.
186 In particular,
187 @code{nil} is a valid HAX handler and ignores all events.
189 In addition,
190 @a[http://common-lisp.net/project/cxml/sax.html#sax]{SAX handlers}
191 are valid HAX handlers (and vice versa), even though
192 hax:abstract-handler and sax:abstract-handler do not
193 share a specific superclass. HAX events sent to SAX handlers are
194 automatically re-signalled as XHTML SAX events, and SAX events sent
195 to HAX handlers are re-signalled as namespace-less HAX events.
197 However, user code should define subclasses of the documented
198 superclasses to enable the HAX/SAX bridging described above.
200 @see{chtml:parse}
201 @see{chtml:serialize-lhtml}
202 @see{chtml:serialize-pt}
203 @see{start-document}
204 @see{end-document}
205 @see{start-element}
206 @see{end-element}
207 @see{characters}
208 @see{comment}")
210 (setf (documentation 'default-handler 'type)
211 "@short{A no-op HAX handler.}
213 This class defines methods for all HAX events that do nothing.
214 It is useful as a superclass when implementing a HAX handler that
215 is interested in only some events and not others.
217 @see{chtml:parse}
218 @see{chtml:serialize-lhtml}
219 @see{chtml:serialize-pt}
220 @see{start-document}
221 @see{end-document}
222 @see{start-element}
223 @see{end-element}
224 @see{characters}
225 @see{comment}")
227 (setf (documentation 'standard-attribute 'type)
228 "@short{An implementation of the HAX attribute protocol.}
230 A standard class implementing the generic functions for HAX
231 attributes. Instances of this class can be passed to
232 @fun{hax:start-element} in the list of attributes.
234 @see-slot{attribute-name}
235 @see-slot{attribute-value}
236 @see-slot{attribute-specified-p}
237 @see-constructor{make-instance}")
239 (setf (documentation 'make-attribute 'function)
240 "@arg[name]{a string/rod}
241 @arg[value]{a string/rod}
242 @arg[specified-p]{a boolean, default is @code{t}}
243 @return{an instance of @class{standard-attribute}.}
244 @short{Creates a HAX attribute.}
246 Creates an instance that can be used with the generic functions
247 for HAX attributes. The result can be passed to
248 @fun{hax:start-element} in the list of attributes.
250 @see{attribute-name}
251 @see{attribute-value}
252 @see{attribute-specified-p}")
254 (setf (documentation 'find-attribute 'function)
255 "@arg[name]{a string/rod}
256 @arg[attrs]{a list of attributes}
257 @return{an attribute, or nil}
258 @short{Searches for an attribute by name.}
260 Returns the first attribute in @var{attrs} with the specified name,
261 or @code{nil} if no such attribute was found.
263 @see{attribute-name}")
265 (setf (documentation 'attribute-name 'function)
266 "@arg[instance]{any class implementing this function}
267 @return{a string/rod}
268 @short{Return an attribute's name.}
270 Instances of this classes implementing this function can be passed to
271 @fun{hax:start-element} in the list of attributes.
273 @see{attribute-value}
274 @see{attribute-specified-p}")
276 (setf (documentation 'attribute-value 'function)
277 "@arg[instance]{any class implementing this function}
278 @return{a string/rod}
279 @short{Return an attribute's value.}
281 Instances of this classes implementing this function can be passed to
282 @fun{hax:start-element} in the list of attributes.
284 @see{attribute-name}
285 @see{attribute-specified-p}")
287 (setf (documentation 'attribute-specified-p 'function)
288 "@arg[instance]{any class implementing this function}
289 @return{a string/rod}
290 @short{Return whether the attribute was contained the parsed document.}
292 Attributes return @code{nil} here if they resulted from a default
293 value declaration in a DTD.
295 Instances of this classes implementing this function can be passed to
296 @fun{hax:start-element} in the list of attributes.
298 @see{attribute-name}
299 @see{attribute-value}")
301 (setf (documentation 'start-document 'function)
302 "@arg[handler]{a HAX/SAX handler
303 (see @class{abstract-handler} for details)}
304 @arg[name]{root element name, a rod/string}
305 @arg[public-id]{nil or the Public ID, a rod/string}
306 @arg[system-id]{nil or the System ID/URI, a rod/string}
307 @return{unspecified}
308 @short{Signals the beginning on an HTML document.}
310 This is the first event sent to any handler.
312 If @var{system-id} is non-nil, the document includes a doctype
313 declaration.
315 @see{start-element}
316 @see{end-element}
317 @see{characters}
318 @see{comment}
319 @see{end-document}")
321 (setf (documentation 'start-element 'function)
322 "@arg[handler]{a HAX/SAX handler
323 (see @class{abstract-handler} for details)}
324 @arg[name]{root element name, a rod/string}
325 @arg[attributes]{a list of attributes}
326 @return{unspecified}
327 @short{Signals the beginning of an HTML element.}
329 This event corresponds to the opening tag of an element.
331 Elements of the attribute list can have any class, but must implement
332 the generic functions for attributes. See @class{standard-attribute}
333 for the built-in attribute implementation.
335 @see{find-attribute}
336 @see{start-document}
337 @see{end-element}
338 @see{characters}
339 @see{comment}
340 @see{end-document}")
342 (setf (documentation 'end-element 'function)
343 "@arg[handler]{a HAX/SAX handler
344 (see @class{abstract-handler} for details)}
345 @arg[name]{root element name, a rod/string}
346 @return{unspecified}
347 @short{Signals the end of an HTML element.}
349 This event corresponds to the closing tag of an element.
351 @see{start-document}
352 @see{start-element}
353 @see{characters}
354 @see{comment}
355 @see{end-document}")
357 (setf (documentation 'characters 'function)
358 "@arg[handler]{a HAX/SAX handler
359 (see @class{abstract-handler} for details)}
360 @arg[data]{rod/string}
361 @return{unspecified}
362 @short{Signals character data.}
364 This event represents character data in a document.
366 @see{start-document}
367 @see{start-element}
368 @see{end-element}
369 @see{comment}
370 @see{end-document}")
372 (setf (documentation 'comment 'function)
373 "@arg[handler]{a HAX/SAX handler
374 (see @class{abstract-handler} for details)}
375 @arg[data]{rod/string}
376 @return{unspecified}
377 @short{Signals a comment.}
379 This event represents a comment.
381 @see{start-document}
382 @see{start-element}
383 @see{end-element}
384 @see{characters}
385 @see{end-document}")
387 (setf (documentation 'end-document 'function)
388 "@arg[handler]{a HAX/SAX handler
389 (see @class{abstract-handler} for details)}
390 @return{The return value of this function depends on the handler class.}
391 @short{Signals the end on an HTML document.}
393 This is the last event sent to any handler, and signals the end of
394 serialization.
396 The return value of this function is usually returned to user code
397 by higher-level serialization functions and can be considered the
398 result of serialization and \"return value\" of the handler.
400 @see{start-document}
401 @see{start-element}
402 @see{end-element}
403 @see{characters}
404 @see{comment}")