adapted from html-gen.slik. the resemblance remains, but is remote.
WITH-XML-WRITER."
(let* ((need-namespaces nil)
(tag-name (etypecase tag
(string tag)
(symbol (progn (setf need-namespaces t) tag))
(abstract-name (progn (setf need-namespaces t) tag))
(cons (if (and (= (length tag) 2) (eq (car tag) 'quote))
(second tag)
(if (consp (first tag))
(second (first tag))
(first tag))))))
(attributes (etypecase tag
(string nil)
(symbol nil)
(abstract-name nil)
(cons (if (and (= (length tag) 2) (eq (car tag) 'quote))
nil (cdr tag)))))
(namespace-bindings
(remove nil
(mapcar #'(lambda (attr)
(when (consp attr)
(destructuring-bind (name value) attr
;; check if a new namespace binding is implied
(etypecase name
(string nil)
((or symbol abstract-name)
(when (string= (namespace-name (namespace name))
*xmlns-prefix-namestring*)
(setf need-namespaces t)
(typecase value
((or string null)
(unless (find-namespace value :if-does-not-exist nil)
(warn "namespace not defined: '~a'" value)))
(t nil))
`(setf *namespace-bindings* (acons ',(local-part name) (find-namespace ,value)
*namespace-bindings*))))))))
attributes))))
(flet ((needs-quoting? (v)
;; this test applies only to static strings in element content...
(not (find-if #'(lambda (c) (find c "<>&")) v))))
(when (symbolp tag-name) (setf tag-name `(quote ,tag-name)))
`(let (,@(when need-namespaces
`((*namespace-bindings* *namespace-bindings*)
(*generated-ns-bindings* nil)))
(*node-level* (1+ *node-level*)))
,@namespace-bindings
(encode-pretty-eol)
(encode-char #\<)
(encode-node ,tag-name)
,@(when attributes
(mapcar #'(lambda (attr)
(cond ((consp attr)
;; if the attribute is a cons, the form is (name value)
(destructuring-bind (name value) attr
(case name
(function `(encode-attribute ,attr))
(t
`(encode-attribute-name-value ',name ,value)))))
(t
`(encode-attribute ,attr))))
attributes))
,@(if need-namespaces
`((when *generated-ns-bindings*
(encode-generated-ns-bindings *generated-ns-bindings*))))
,(if content '(encode-char #\>) '(encode-string " />"))
;; if the form is an expression, then presume it is to be included for the side effect
;; a string or a symbol is written directly
,@(mapcar #'(lambda (form)
(if (consp form)
form
(if (and (stringp form) (not (needs-quoting? form)))
`(encode-string ,form)
`(encode-node ,form))))
content)
,@(when content
`((encode-pretty-eol)
(encode-string "")
(encode-node ,tag-name)
(encode-string ">")))))))
#|
(with-xml-writer (*trace-output*)
(xml tag "test 1 2 3"))
(with-xml-writer (*trace-output*)
(xml "tag" "test 1 2 3"))
(with-xml-writer (*trace-output*)
(xml ({xhtml}tag ({xmlns}|| "http://www.w3.org/1999/xhtml")) "test 1 2 3"))
(with-xml-writer (*trace-output*)
(xml ("tag" ({xmlns}|| "http://www.w3.org/1999/xhtml") ("attr" "v1")) "test 1 2 3"))
(flet ((do-the-element (attribute-1 attribute-2 attribute-3)
(xml ("tag"
({xmlns}|| "http://www.w3.org/1999/xhtml")
#'(lambda () (encode-attribute-name-value "qwer" "asdf"))
attribute-1 attribute-2 attribute-3)
"a literal string"))
(do-an-attribute ()
(encode-attribute-name-value "one" "two"))
(do-an-attribute-value ()
(encode-string "generated the string")))
(with-xml-writer (*trace-output*)
(do-the-element (list "literalname" "literal value"
"nameForGeneratedString" #'do-an-attribute-value)
#'do-an-attribute
nil)))
(with-xml-writer (*trace-output*)
(format *xml-writer-stream* "test ~a" 1))
|#
:EOF