1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
3 ;;; Copyright (c) 2007 David Lichteblau. All rights reserved.
5 ;;; Redistribution and use in source and binary forms, with or without
6 ;;; modification, are permitted provided that the following conditions
9 ;;; * Redistributions of source code must retain the above copyright
10 ;;; notice, this list of conditions and the following disclaimer.
12 ;;; * Redistributions in binary form must reproduce the above
13 ;;; copyright notice, this list of conditions and the following
14 ;;; disclaimer in the documentation and/or other materials
15 ;;; provided with the distribution.
17 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
18 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 (in-package :cxml-stp-impl
)
32 (declaim (optimize (debug 2)))
35 ;;;; Class PARENT-NODE
39 (defgeneric (setf base-uri
) (newval node
)
41 "@arg[newval]{string, the new base URI}
42 @arg[node]{an @class{parent-node}}
43 @return{the new base URI}
44 @short{Sets the node's base URI.}"))
46 (defgeneric %base-uri
(node))
47 (defmethod %base-uri
((node node
)) (or (slot-value node
'%base-uri
) ""))
48 (defmethod (setf %base-uri
) (newval (node node
))
50 (when (and (plusp (length newval
))
52 (not (search "://" newval
)))
53 (warn "base URI does not look like an absolute URL: ~S" newval
))
54 (setf (slot-value node
'%base-uri
) (or newval
"")))
56 (defun maybe-fill-in-base-uri (removed-child)
57 (when (typep removed-child
'element
)
58 (fill-in-base-uri removed-child
)))
60 (defun fill-in-base-uri (removed-child)
61 (setf (%base-uri removed-child
)
62 (find-base-uri removed-child
)))
64 (defun find-base-uri (node)
66 for n
= node then parent
67 for parent
= (parent n
)
68 for uri
= (%base-uri n
)
69 while
(and (equal uri
"") parent
)
70 finally
(return uri
)))
72 (defgeneric (setf base-uri
) (newval node
))
78 ;;; CHILDREN-related methods on NODE
80 (defmethod map-children (result-type fn
(node parent-node
))
81 (map result-type fn
(%children node
)))
84 ;;; CHILDREN-related convenience functions
86 (defun prepend-child (parent child
)
87 "@arg[parent]{a @class{parent-node}}
88 @arg[child]{a @class{node}}
89 @short{Adds @code{child} as the first child of @code{parent}, if allowed.}
91 Signals an error if the child already has a parent."
92 (insert-child parent child
0))
94 (defun append-child (parent child
)
95 "@arg[child]{a @class{node}}
96 @arg[parent]{a @class{parent-node}}
97 Adds @code{child} as the last child of @code{parent}, if allowed.
99 Signals an error if the child already has a parent."
100 (insert-child parent child
(length (%children parent
))))
102 (defun delete-nth-child (idx parent
)
103 "@arg[idx]{a non-negative integer}
104 @arg[parent]{a @class{parent-node}}
105 Removes child @code{idx} of @code{parent}, if allowed."
106 (let ((old (%children parent
)))
109 (delete-child-if (constantly t
) parent
:start idx
:count
1))))
111 (defun delete-child (child parent
&key from-end test start end count key
)
112 "@arg[child]{an object}
113 @arg[parent]{a @class{node}}
114 @arg[from-end]{a generalized boolead}
115 @arg[start, end]{bounding index designators for @code{parent}'s child list}
116 @arg[key]{a designator for a function of one argument, or nil}
117 @arg[test]{a designator for a function of two arguments, or nil}
118 @return{a @class{node} or nil}
119 Searches for a child node of @code{parent} that satisfies the @code{test}
120 and removes it, if allowed."
121 (setf test
(or test
#'eql
))
122 (delete-child-if (lambda (c) (funcall test child c
))
130 (defun insert-child-before (parent new-child ref-child
)
131 "@arg[parent]{a @class{parent-node}}
132 @arg[new-child]{a @class{node}}
133 @arg[ref-child]{a @class{node}}
134 @short{Adds @code{new-child} before @code{ref-child} as a child node of
135 @code{parent}, if allowed.}
137 Signals an error if the child already has a parent.
139 Also signals an error if @code{ref-child} is not a child of @code{parent}."
140 (let ((idx (child-position ref-child parent
)))
142 (stp-error "referenced child not found: ~A" ref-child
))
143 (insert-child parent new-child idx
)))
145 (defun insert-child-after (parent new-child ref-child
)
146 "@arg[parent]{a @class{parent-node}}
147 @arg[new-child]{a @class{node}}
148 @arg[ref-child]{a @class{node}}
149 @short{Adds @code{new-child} after @code{ref-child} as a child node of
150 @code{parent}, if allowed.}
152 Signals an error if the child already has a parent.
154 Also signals an error if @code{ref-child} is not a child of @code{parent}."
155 (let ((idx (child-position ref-child parent
)))
157 (stp-error "referenced child not found: ~A" ref-child
))
158 (insert-child parent new-child
(1+ idx
))))
160 ;;; CHILDREN-related functions we define
162 (defgeneric insert-child
(parent child position
)
164 "@arg[parent]{a @class{parent-node}}
165 @arg[child]{a @class{node}}
166 @arg[position]{a non-negative integer}
167 @short{Adds @code{child} as a child node of @code{parent} at position
168 @code{position} if allowed.}
170 Signals an error if the new child already has a parent.
172 Also signals an error if @code{position} is greater than the number
173 @code{parent}'s child nodes."))
175 (defgeneric delete-child-if
176 (predicate parent
&rest args
&key from-end start end count key
)
178 "@arg[predicate]{a designator for a function of one argument that returns
179 a generalized boolean}
180 @arg[parent]{a @class{node}}
181 @arg[from-end]{a generalized boolead}
182 @arg[start, end]{bounding index designators for @code{parent}'s child list}
183 @arg[key]{a designator for a function of one argument, or nil}
184 @arg[test]{a designator for a function of two arguments, or nil}
185 @return{a @class{node} or nil}
186 Searches for an child node of @code{parent} that satisfies @code{predicate}
187 and removes it, if allowed."))
189 (defgeneric replace-child
(parent old-child new-child
)
191 "@arg[parent]{a @class{parent-node}}
192 @arg[old-child]{a @class{node}}
193 @arg[new-child]{a @class{node}}
194 @short{Adds @code{new-child} instead of @code{old-child} as a child node of
195 @code{parent}, if allowed.}
197 Signals an error if the new child already has a parent.
199 Also signals an error if @code{old-child} is not a child of
202 (defgeneric check-insertion-allowed
(parent child position
))
203 (defgeneric check-deletion-allowed
(parent child position
))
205 (defmethod insert-child ((parent parent-node
) (child node
) i
)
206 (check-insertion-allowed parent child i
)
207 (%unchecked-insert-child parent child i
)
208 (setf (%parent child
) parent
))
210 (defmethod replace-child ((parent parent-node
) old-child new-child
)
211 (check-type old-child node
)
212 (check-type new-child node
)
213 (let ((idx (child-position old-child parent
)))
215 (stp-error "old child not found: ~A" old-child
))
216 (unless (eql old-child new-child
)
217 (check-insertion-allowed parent new-child idx
)
218 (delete-nth-child idx parent
)
219 (%unchecked-insert-child parent new-child idx
))))
221 (defun %unchecked-insert-child
(parent child i
)
222 (unless (%children parent
)
223 (setf (%children parent
) (make-array 1 :fill-pointer
0 :adjustable t
)))
224 (let ((children (%children parent
)))
225 (cxml-dom::make-space children
1)
226 (cxml-dom::move children children i
(1+ i
) (- (length children
) i
))
227 (incf (fill-pointer children
))
228 (setf (elt children i
) child
))
229 (setf (%parent child
) parent
))
231 (defun %nuke-nth-child
(parent i
)
232 (let* ((c (%children parent
))
234 (maybe-fill-in-base-uri loser
)
235 (cxml-dom::move c c
(1+ i
) i
(- (length c
) i
1))
236 (decf (fill-pointer c
))
237 (setf (%parent loser
) nil
)))
239 (defmethod delete-child-if
240 (predicate (parent parent-node
) &key from-end start end count key
)
241 (let ((c (%children parent
))
243 (setf start
(or start
0))
244 (setf key
(or key
#'identity
))
245 (setf count
(or count
(length c
)))
246 (setf end
(or end
(length c
)))
247 (unless (and (<= 0 start
(length c
))
250 (stp-error "invalid bounding index designators"))
251 (when c
;nothing to delete if not a vector yet
254 (cxml::while
(and (>= i start
) (plusp count
))
255 (let ((loser (elt c i
)))
256 (when (funcall predicate
(funcall key loser
))
257 (check-deletion-allowed parent loser i
)
258 (maybe-fill-in-base-uri loser
)
259 (cxml-dom::move c c
(1+ i
) i
(- (length c
) i
1))
260 (decf (fill-pointer c
))
261 (setf (%parent loser
) nil
)
265 (let ((tbd (- end start
))
267 (cxml::while
(and (plusp tbd
) (plusp count
))
268 (let ((loser (elt c i
)))
270 ((funcall predicate
(funcall key loser
))
271 (check-deletion-allowed parent loser i
)
272 (maybe-fill-in-base-uri loser
)
273 (cxml-dom::move c c
(1+ i
) i
(- (length c
) i
1))
274 (decf (fill-pointer c
))
275 (setf (%parent loser
) nil
)
283 (defreader parent-node
((base-uri "") (children nil
))
284 (setf (%base-uri this
) base-uri
)
285 (dolist (child children
)
286 (append-child this child
)))