Fixed (setf document-element) in the presence of non-element children
[cxml-stp.git] / xpath.lisp
blob46d50d84317e9b3dcc3fa697c7cdb30015f7889c
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
3 ;;; Copyright (c) 2007 Ivan Shvedunov. All rights reserved.
4 ;;; Copyright (c) 2007 David Lichteblau. All rights reserved.
6 ;;; Redistribution and use in source and binary forms, with or without
7 ;;; modification, are permitted provided that the following conditions
8 ;;; are met:
9 ;;;
10 ;;; * Redistributions of source code must retain the above copyright
11 ;;; notice, this list of conditions and the following disclaimer.
12 ;;;
13 ;;; * Redistributions in binary form must reproduce the above
14 ;;; copyright notice, this list of conditions and the following
15 ;;; disclaimer in the documentation and/or other materials
16 ;;; provided with the distribution.
17 ;;;
18 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
19 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 (in-package :cxml-stp-impl)
33 (defun vector->pipe (vector &optional (start 0))
34 (if (>= start (length vector))
35 nil
36 (xpath::make-pipe (elt vector start)
37 (vector->pipe vector (1+ start)))))
40 ;;;; XPath protocol implementation for STP
42 ;;;; FIXME: xpath-protocol:child-pipe destructively normalizes the STP tree!
44 (define-default-method xpath-protocol:local-name ((node stp:node))
45 (local-name node))
47 (define-default-method xpath-protocol:namespace-prefix ((node stp:node))
48 (namespace-prefix node))
50 (define-default-method xpath-protocol:parent-node ((node stp:node))
51 (stp:parent node))
53 (define-default-method xpath-protocol:namespace-uri ((node stp:node))
54 (namespace-uri node))
56 (define-default-method xpath-protocol:qualified-name ((node stp:node))
57 (qualified-name node))
59 (define-default-method xpath-protocol:base-uri ((node stp:element))
60 (stp:base-uri node))
62 (define-default-method xpath-protocol:base-uri ((node stp:document))
63 (stp:base-uri node))
65 (define-default-method xpath-protocol:base-uri ((node stp:node))
66 nil)
68 (define-default-method xpath-protocol:child-pipe ((node stp:node))
69 nil)
71 (define-default-method xpath-protocol:child-pipe ((node stp:document))
72 (filter-children (alexandria:of-type '(not document-type)) node))
74 (define-default-method xpath-protocol:child-pipe ((node stp:element))
75 (normalize-text-nodes! node)
76 (vector->pipe (%children node)))
78 (define-default-method xpath-protocol:attribute-pipe ((node stp:node))
79 nil)
81 (define-default-method xpath-protocol:attribute-pipe ((node stp:element))
82 (list-attributes node))
84 (define-default-method xpath-protocol:namespace-pipe ((node stp:node))
85 (when (stp:parent node)
86 (xpath-protocol:namespace-pipe (stp:parent node))))
88 (defstruct (stp-namespace
89 (:constructor make-stp-namespace (parent prefix uri)))
90 parent
91 prefix
92 uri)
94 (define-default-method xpath-protocol:node-equal
95 ((a stp-namespace) (b stp-namespace))
96 (and (eq (stp-namespace-parent a) (stp-namespace-parent b))
97 (equal (stp-namespace-prefix a) (stp-namespace-prefix b))))
99 (define-default-method xpath-protocol:hash-key
100 ((node stp-namespace))
101 (cons (stp-namespace-parent node) (stp-namespace-prefix node)))
103 (define-default-method xpath-protocol:base-uri ((node stp-namespace))
104 nil)
106 (define-default-method xpath-protocol:node-p ((node stp-namespace))
109 (define-default-method xpath-protocol:child-pipe ((node stp-namespace)) nil)
110 (define-default-method xpath-protocol:attribute-pipe ((node stp-namespace)) nil)
111 (define-default-method xpath-protocol:namespace-pipe ((node stp-namespace)) nil)
113 (define-default-method xpath-protocol:parent-node ((node stp-namespace))
114 (stp-namespace-parent node))
115 (define-default-method xpath-protocol:local-name ((node stp-namespace))
116 (stp-namespace-prefix node))
117 (define-default-method xpath-protocol:qualified-name ((node stp-namespace))
118 (stp-namespace-prefix node))
119 (define-default-method xpath-protocol:namespace-uri ((node stp-namespace))
122 (define-default-method xpath-protocol:namespace-pipe
123 ((original-node stp:element))
124 (let ((node original-node)
125 (table (make-hash-table :test 'equal))
126 (current '()))
127 (labels ((yield (prefix uri)
128 (unless (gethash prefix table)
129 (let ((nsnode (make-stp-namespace original-node prefix uri)))
130 (setf (gethash prefix table) nsnode)
131 (push nsnode current))))
132 (iterate ()
133 (if current
134 (cons (pop current) #'iterate)
135 (recurse)))
136 (recurse ()
137 (etypecase node
138 (null)
139 (stp:element
140 (let ((parent (stp:parent node)))
141 (map-extra-namespaces #'yield node)
142 (unless (and (zerop (length (%namespace-prefix node)))
143 (zerop (length (%namespace-uri node)))
144 (or (typep parent 'stp:document)
145 (zerop
146 (length
147 (stp:find-namespace "" parent)))))
148 (yield (%namespace-prefix node)
149 (%namespace-uri node)))
150 (dolist (a (%attributes node))
151 (when (plusp (length (namespace-prefix a)))
152 (yield (namespace-prefix a) (namespace-uri a))))
153 (setf node parent))
154 (iterate))
155 (stp:document
156 (yield "xml" "http://www.w3.org/XML/1998/namespace")
157 #+nil (yield "xmlns" "http://www.w3.org/2000/xmlns/")
158 (setf node nil)
159 (iterate)))))
160 (recurse))))
162 (define-default-method xpath-protocol:node-text ((node node))
163 (string-value node))
165 (define-default-method xpath-protocol:node-text ((node stp-namespace))
166 (stp-namespace-uri node))
168 (define-default-method xpath-protocol:node-p ((node node))
171 (define-default-method xpath-protocol:node-type-p ((node node) type)
172 (declare (ignore type))
173 nil)
175 (define-default-method xpath-protocol:node-type-p ((node stp-namespace) type)
176 (declare (ignore type))
177 nil)
179 (macrolet ((deftypemapping (class keyword)
180 `(define-default-method xpath-protocol:node-type-p
181 ((node ,class) (type (eql ,keyword)))
182 t)))
183 (deftypemapping document :document)
184 (deftypemapping comment :comment)
185 (deftypemapping processing-instruction :processing-instruction)
186 (deftypemapping text :text)
187 (deftypemapping attribute :attribute)
188 (deftypemapping element :element)
189 (deftypemapping stp-namespace :namespace))
191 (defun normalize-text-nodes! (node)
192 (when (typep node 'stp:parent-node)
193 (let ((children (%children node)))
194 (when (loop
195 for child across children
196 for a = nil then b
197 for b = (typep child 'text)
198 thereis (and b (or a (zerop (length (stp:data child))))))
199 (let ((previous nil)
200 (results '()))
201 (stp:do-children (child node)
202 (cond
203 ((not (typep child 'stp:text))
204 (when previous
205 (push (stp:make-text
206 (apply #'concatenate 'string (nreverse previous)))
207 results)
208 (setf (%parent (car results)) node)
209 (setf previous nil))
210 (push child results))
211 (previous
212 (push (stp:data child) previous))
213 ((zerop (length (stp:data child))))
215 (setf previous (list (stp:data child))))))
216 (when previous
217 (push (stp:make-text
218 (apply #'concatenate 'string (nreverse previous)))
219 results)
220 (setf (%parent (car results)) node))
221 (setf (cxml-stp-impl::%children node)
222 (let ((n (length results)))
223 (make-array n
224 :fill-pointer n
225 :initial-contents (nreverse results)))))))))
227 (define-default-method xpath-protocol:get-element-by-id ((node stp:node) id)
228 (let* ((document (stp:document node))
229 (dtd (when (stp:document-type document)
230 (stp:dtd (stp:document-type document)))))
231 (when dtd
232 (block nil
233 (flet ((test (node)
234 (when (typep node 'stp:element)
235 (let ((elmdef
236 (cxml::find-element (stp:qualified-name node) dtd)))
237 (when elmdef
238 (dolist (attdef (cxml::elmdef-attributes elmdef))
239 (when (eq :ID (cxml::attdef-type attdef))
240 (let* ((name (cxml::attdef-name attdef))
241 (value (stp:attribute-value node name)))
242 (when (and value (equal value id))
243 (return node))))))))))
244 (find-recursively-if #'test document))))))
246 (define-default-method xpath-protocol:unparsed-entity-uri
247 ((node stp:node) name)
248 (let ((doctype (stp:document-type (stp:document node))))
249 (when doctype
250 (let ((dtd (stp:dtd doctype)))
251 (when dtd
252 (let ((entdef (cdr (gethash name (cxml::dtd-gentities dtd)))))
253 (when (typep entdef 'cxml::external-entdef)
254 (let ((uri (cxml::extid-system (cxml::entdef-extid entdef))))
255 (when uri
256 (puri:render-uri uri nil))))))))))
258 (define-default-method xpath-protocol:local-name ((node stp:text)) "")
260 (define-default-method xpath-protocol:namespace-prefix ((node stp:text)) "")
262 (define-default-method xpath-protocol:namespace-uri ((node stp:text)) "")
264 (define-default-method xpath-protocol:qualified-name ((node stp:text)) "")
266 (define-default-method xpath-protocol:local-name ((node stp:comment)) "")
268 (define-default-method xpath-protocol:namespace-prefix ((node stp:comment)) "")
270 (define-default-method xpath-protocol:namespace-uri ((node stp:comment)) "")
272 (define-default-method xpath-protocol:qualified-name
273 ((node stp:comment))
276 (define-default-method xpath-protocol:namespace-prefix
277 ((node stp:processing-instruction))
280 (define-default-method xpath-protocol:local-name
281 ((node stp:processing-instruction))
282 (stp:target node))
284 (define-default-method xpath-protocol:qualified-name
285 ((node stp:processing-instruction))
286 (stp:target node))
288 (define-default-method xpath-protocol:namespace-uri
289 ((node stp:processing-instruction))
292 (define-default-method xpath-protocol:namespace-prefix ((node stp:document))
295 (define-default-method xpath-protocol:qualified-name ((node stp:document)) "")
297 (define-default-method xpath-protocol:local-name ((node stp:document)) "")
299 (define-default-method xpath-protocol:processing-instruction-target
300 ((node stp:node))
301 node)
303 (define-default-method xpath-protocol:processing-instruction-target
304 ((node stp:processing-instruction))
305 (stp:target node))
307 (defun run-xpath-tests ()
308 (let ((xpath::*dom-builder* (stp:make-builder))
309 (xpath::*document-element* #'stp:document-element))
310 (xpath::run-all-tests)))