1 ;;;; xml-compat.lisp -- XMLS-compatible data structures
3 ;;;; This file is part of the CXML parser, released under Lisp-LGPL.
4 ;;;; See file COPYING for details.
6 ;;;; Developed 2004 for headcraft - http://headcraft.de/
7 ;;;; Copyright: David Lichteblau
11 (:export
#:make-node
#:node-name
#:node-ns
#:node-attrs
#:node-children
12 #:make-xmls-builder
#:map-node
#:make-xpath-navigator
))
14 (in-package :cxml-xmls
)
19 (defun make-node (&key name ns attrs children
)
20 `(,(if ns
(cons name ns
) name
)
24 (defun node-name (node)
25 (let ((car (car node
)))
30 (defun (setf node-name
) (newval node
)
31 (let ((car (car node
)))
33 (setf (car car
) newval
)
34 (setf (car node
) newval
))))
37 (let ((car (car node
)))
42 (defun (setf node-ns
) (newval node
)
43 (let ((car (car node
)))
45 (setf (cdr car
) newval
)
46 (setf (car node
) (cons car newval
)))
49 (defun node-attrs (node)
52 (defun (setf node-attrs
) (newval node
)
53 (setf (cadr node
) newval
))
55 (defun node-children (node)
58 (defun (setf node-children
) (newval node
)
59 (setf (cddr node
) newval
))
62 ;;;; SAX-Handler (Parser)
64 (defclass xmls-builder
(sax:default-handler
)
65 ((element-stack :initform nil
:accessor element-stack
)
66 (root :initform nil
:accessor root
)
67 (include-default-values :initform t
68 :initarg
:include-default-values
69 :accessor include-default-values
)
70 (include-namespace-uri :initform t
71 :initarg
:include-namespace-uri
72 :accessor include-namespace-uri
)))
74 (defun make-xmls-builder (&key
(include-default-values t
)
75 (include-namespace-uri t
))
76 "Make a XMLS style builder. When 'include-namespace-uri is true a modified
77 XMLS tree is generated that includes the element namespace URI rather than
78 the qualified name prefix and also includes the namespace URI for attributes."
79 (make-instance 'xmls-builder
80 :include-default-values include-default-values
81 :include-namespace-uri include-namespace-uri
))
83 (defmethod sax:end-document
((handler xmls-builder
))
86 (defmethod sax:start-element
87 ((handler xmls-builder
) namespace-uri local-name qname attributes
)
88 (let* ((include-default-values (include-default-values handler
))
89 (include-namespace-uri (include-namespace-uri handler
))
92 for attr in attributes
93 for attr-namespace-uri
= (sax:attribute-namespace-uri attr
)
94 for attr-local-name
= (sax:attribute-local-name attr
)
95 when
(and (or (sax:attribute-specified-p attr
)
96 include-default-values
)
98 (or (not include-namespace-uri
)
99 (not attr-namespace-uri
)
102 (list (cond (include-namespace-uri
103 (cond (attr-namespace-uri
104 (cons attr-local-name attr-namespace-uri
))
106 (sax:attribute-qname attr
))))
108 (sax:attribute-qname attr
)))
109 (sax:attribute-value attr
))))
110 (namespace (when include-namespace-uri namespace-uri
))
111 (node (make-node :name local-name
114 (parent (car (element-stack handler
))))
116 (push node
(node-children parent
))
117 (setf (root handler
) node
))
118 (push node
(element-stack handler
))))
120 (defmethod sax:end-element
121 ((handler xmls-builder
) namespace-uri local-name qname
)
122 (declare (ignore namespace-uri local-name qname
))
123 (let ((node (pop (element-stack handler
))))
124 (setf (node-children node
) (reverse (node-children node
)))))
126 (defmethod sax:characters
((handler xmls-builder
) data
)
127 (let* ((parent (car (element-stack handler
)))
128 (prev (car (node-children parent
))))
129 ;; Be careful to accept both rods and strings here, so that xmls can be
130 ;; used with strings even if cxml is configured to use octet string rods.
131 (if (typep prev
'(or rod string
))
132 ;; um entities herum wird SAX:CHARACTERS mehrfach aufgerufen fuer
133 ;; den gleichen Textknoten. Hier muessen wir den bestehenden Knoten
134 ;; erweitern, sonst ist das Dokument nicht normalisiert.
135 ;; (XXX Oder sollte man besser den Parser entsprechend aendern?)
136 (setf (car (node-children parent
))
137 (concatenate `(vector ,(array-element-type prev
))
140 (push data
(node-children parent
)))))
143 ;;;; SAX-Treiber (fuer Serialisierung)
147 &key
(include-xmlns-attributes sax
:*include-xmlns-attributes
*)
148 (include-namespace-uri t
))
149 (if include-namespace-uri
150 (map-node/lnames
(cxml:make-namespace-normalizer handler
)
152 include-xmlns-attributes
)
153 (map-node/qnames handler node include-xmlns-attributes
)))
155 (defun map-node/lnames
(handler node include-xmlns-attributes
)
156 (sax:start-document handler
)
157 (labels ((walk (node)
158 (unless (node-ns node
)
159 (error "serializing with :INCLUDE-NAMESPACE-URI, but node ~
160 was created without namespace URI"))
162 (compute-attributes/lnames node include-xmlns-attributes
))
164 (lname (node-name node
))
165 (qname lname
) ;let the normalizer fix it
167 (sax:start-element handler uri lname qname attlist
)
168 (dolist (child (node-children node
))
172 (sax:characters handler
(string-rod child
)))))
173 (sax:end-element handler uri lname qname
))))
175 (sax:end-document handler
))
177 (defun map-node/qnames
(handler node include-xmlns-attributes
)
178 (sax:start-document handler
)
179 (labels ((walk (node)
181 (error "serializing without :INCLUDE-NAMESPACE-URI, but node ~
182 was created with a namespace URI"))
184 (compute-attributes/qnames node include-xmlns-attributes
))
185 (qname (string-rod (node-name node
)))
186 (lname (nth-value 1 (cxml::split-qname qname
))))
187 (sax:start-element handler nil lname qname attlist
)
188 (dolist (child (node-children node
))
192 (sax:characters handler
(string-rod child
)))))
193 (sax:end-element handler nil lname qname
))))
195 (sax:end-document handler
))
197 (defun compute-attributes/lnames
(node xmlnsp
)
200 (destructuring-bind (name value
) a
202 (setf name
(cons name nil
)))
203 (destructuring-bind (lname &rest uri
) name
205 ((not (equal uri
"http://www.w3.org/2000/xmlns/"))
207 ;; let the normalizer fix the qname
209 (string-rod (concatenate 'string
213 :local-name
(string-rod lname
)
215 :value
(string-rod value
)
221 (concatenate 'string
"xmlns:" lname
)
223 :local-name
(string-rod lname
)
225 :value
(string-rod value
)
229 (defun compute-attributes/qnames
(node xmlnsp
)
232 (destructuring-bind (name value
) a
234 (error "serializing without :INCLUDE-NAMESPACE-URI, ~
235 but attribute was created with a namespace ~
238 (not (cxml::xmlns-attr-p
(string-rod name
))))
239 (sax:make-attribute
:qname
(string-rod name
)
240 :value
(string-rod value
)
247 (defun make-xpath-navigator ()
248 (make-instance 'xpath-navigator
))
250 (defclass xpath-navigator
()
251 ((parents :initform
(make-hash-table))
252 (prefixes :initform
(make-hash-table))
253 (children :initform
(make-hash-table))
254 (attributes :initform
(make-hash-table))
255 (namespaces :initform
(make-hash-table))))
257 (defmethod initialize-instance :after
((instance xpath-navigator
) &key
)
258 (with-slots (prefixes) instance
259 (setf (gethash "http://www.w3.org/XML/1998/namespace" prefixes
)