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-types
)
31 (defstruct (param (:constructor make-param
(name value
)))
32 "@short{A named data type parameter.}
34 (With the XSD type library, parameters are known as restricting facets.)
35 @see-constructor{make-param}
37 @see{cxml-rng:pattern-params}
40 @see-slot{param-value}"
44 (setf (documentation 'make-param
'function
)
45 "@arg[name]{paramater name, a string}
46 @arg[value]{paramater value, a string}
47 @return{a @class{param}}
48 Create a data type parameter.
52 (setf (documentation 'param-name
'function
)
53 "@arg[instance]{an instance of @class{param}}
55 The data type parameter's name.
58 (setf (documentation 'param-value
'function
)
59 "@arg[instance]{an instance of @class{param}}
61 The data type parameter's value.
64 (defclass data-type
() ()
66 "@short{The abstract superclass of all types.}
68 Each type belongs to a datatype library, named by a keyword. In each
69 library, the types are named by strings.
71 @see-constructor{find-type}
73 @see-slot{type-library}
74 @see-slot{type-context-dependent-p}
76 @see{equal-using-type}
77 @see{lessp-using-type}
80 (defgeneric find-type
(library name params
)
82 "@arg[library]{datatype library, a keyword symbol}
83 @arg[name]{the type's name, a string}
84 @arg[params]{type parameters, a list of @class{param} instances}
85 @return{an instance of @class{data-type}, or @code{nil}}
86 @short{Look up the type named @em{name} in datatype library @em{library}.}
88 Return a type instance for this type and the additional parameters,
89 or @code{nil} if the type does not exist.
91 Additional parameters (knows as restricting facets in XSD) can be passed
92 to specify or restrict the type for the purposes of @fun{validp}.
96 (defgeneric type-library
(type)
98 "@arg[type]{an instance of @class{data-type}}
99 @return{library name, a keyword}
100 @short{Return the name of the library this type belongs to.}
103 @see{type-context-dependent-p}"))
105 (defgeneric type-name
(type)
107 "@arg[type]{an instance of @class{data-type}}
108 @return{type name, a string}
109 @short{Return the name this type has within its library.}
112 @see{type-context-dependent-p}"))
114 (defmethod find-type ((library t
) name params
)
115 (declare (ignore name params
))
118 (defgeneric type-context-dependent-p
(type)
120 "@arg[type]{an instance of @class{data-type}}
122 @short{Return true if parsing and validation of values by this type
123 depends on the validation context.}
125 In this case, the optional @code{context} argument to @fun{parse} and
126 @fun{validp} is required, and an error will be signalled if it is missing.
128 @see{validation-context}
131 @see{type-context-dependent-p}"))
133 (defmethod type-context-dependent-p ((type data-type
))
136 (defgeneric equal-using-type
(type u v
)
138 "@arg[type]{an instance of @class{data-type}}
139 @arg[u]{a parsed value as returned by @fun{parse}}
140 @arg[v]{a parsed value as returned by @fun{parse}}
142 @short{Compare the @emph{values} @code{u} and @code{v} using a
143 data-type-dependent equality function.}
147 (defgeneric parse
(type e
&optional context
)
149 "@arg[type]{an instance of @class{data-type}}
151 @arg[context]{an instance of @class{validation-context}}
153 @short{Parse string @code{e} and return a representation of its value
154 as defined by the data type.}
156 The @code{context} argument is required if @fun{type-context-dependent-p}
157 is true for @code{type}, and will be ignored otherwise.
159 @see{equal-using-type}
162 (defgeneric validp
(type e
&optional context
)
164 "@arg[type]{an instance of @class{data-type}}
166 @arg[context]{an instance of @class{validation-context}}
168 @short{Determine whether a string is a valid lexical representation
171 The @code{context} argument is required if @fun{type-context-dependent-p}
172 is true for @code{type}, and will be ignored otherwise.
175 @see{equal-using-type}"))
178 ;;; Validation context
180 (defclass validation-context
() ()
182 "@short{This abstract class defines a protocol allowing data types
183 to query the XML parser about its current state.}
185 Some types are context dependent, as indicated by
186 @fun{type-context-dependent-p}. Those types need access to state
187 computed by the XML parser implicitly, like namespace bindings or
190 User-defined subclasses must implement methods
191 for the functions @fun{context-find-namespace-binding} and
192 @fun{context-find-unparsed-entity}.
194 Two pre-defined validation context implementations are
195 provided, one for use with SAX, the other based on Klacks."))
197 (defgeneric context-find-namespace-binding
(context prefix
)
199 "@arg[context]{an instance of @class{validation-context}}
200 @arg[prefix]{name prefix, a string}
201 @return{the namespace URI as a string, or NIL}
202 @short{This function resolves a namespace prefix to a namespace URI in the
204 All currently declared namespaces
205 are taken into account, including those declared directly on the
208 (defgeneric context-find-unparsed-entity
(context name
)
210 "@arg[context]{an instance of @class{validation-context}}
211 @arg[name]{entity name, a string}
212 @return{@code{nil}, or a list of public id, system id, and notation name}
213 This function looks for an unparsed entity in the current context."))
215 (defclass klacks-validation-context
(validation-context)
216 ((source :initarg
:source
:accessor context-source
))
218 "A validation-context implementation that queries
219 a klacks source for information about the parser's current state.
220 @see-constructor{make-klacks-validation-context}"))
222 (defun make-klacks-validation-context (source)
223 "@arg[source]{a @a[http://common-lisp.net/project/cxml/klacks.html]{
225 @return{a @class{klacks-validation-context}}
226 Create a validation-context that will query the given klacks source for
227 the current parser context."
228 (make-instance 'klacks-validation-context
:source source
))
230 (defmethod context-find-namespace-binding
231 ((context klacks-validation-context
) prefix
)
232 (klacks:find-namespace-binding prefix
(context-source context
)))
235 (defmethod context-find-unparsed-entity
236 ((context klacks-validation-context
) name
)
237 (or (dolist (x (slot-value (context-source context
)
238 'cxml
::external-declarations
))
239 (when (and (eq (car x
) 'sax
:unparsed-entity-declaration
)
240 (equal (cadr x
) name
))
242 (dolist (x (slot-value (context-source context
)
243 'cxml
::internal-declarations
))
244 (when (and (eq (car x
) 'sax
:unparsed-entity-declaration
)
245 (equal (cadr x
) name
))
248 (defclass sax-validation-context-mixin
(validation-context)
249 ((stack :initform nil
:accessor context-stack
)
250 (unparsed-entities :initform
(make-hash-table :test
'equal
)
251 :accessor unparsed-entities
))
253 "@short{A class that implements validation-context as a mixin for
254 user-defined SAX handler classes.}
256 The mixin will record namespace information
257 automatically, and the user's SAX handler can simply be passed as a
258 validation context to data type functions."))
260 (defmethod sax:start-prefix-mapping
261 ((handler sax-validation-context-mixin
) prefix uri
)
262 (push (cons prefix uri
) (context-stack handler
)))
264 (defmethod sax:end-prefix-mapping
265 ((handler sax-validation-context-mixin
) prefix
)
266 (setf (context-stack handler
)
268 (context-stack handler
)
273 (defmethod sax:unparsed-entity-declaration
274 ((context sax-validation-context-mixin
)
275 name public-id system-id notation-name
)
276 (setf (gethash name
(unparsed-entities context
))
277 (list public-id system-id notation-name
)))
279 (defmethod context-find-namespace-binding
280 ((context sax-validation-context-mixin
) prefix
)
281 (cdr (assoc prefix
(context-stack context
) :test
#'equal
)))
283 (defmethod context-find-unparsed-entity
284 ((context sax-validation-context-mixin
) name
)
285 (gethash name
(unparsed-entities context
)))
288 ;;; Relax NG built-in type library
290 (defclass rng-type
(data-type) ()
292 "@short{The class of Relax NG built-in types.}
293 Relax NG defines two built-in data type: string and token.
295 The Relax NG type library is named @code{:||}."))
297 (defclass string-type
(rng-type) ()
299 "@short{The Relax NG 'string' type.}
300 This data type allows arbitrary strings and interprets them as-is.
302 For this type, @fun{parse} will return any string unchanged, and
303 @fun{equal-using-type} compares strings using @code{equal}."))
305 (defclass token-type
(rng-type) ()
307 "@short{The Relax NG 'token' type.}
308 This data type allows arbitrary strings and normalizes all whitespaces.
310 For this type, @fun{parse} will return the string with leading and
311 trailing whitespace removed, and remaining sequences of spaces
312 compressed down to one space character each.
314 A method for @fun{equal-using-type} compares strings using @code{equal}."))
316 (defmethod type-library ((type rng-type
))
319 (defvar *string-data-type
* (make-instance 'string-type
))
320 (defvar *token-data-type
* (make-instance 'token-type
))
322 (defmethod find-type ((library (eql :||
)) name params
)
326 ((equal name
"string") *string-data-type
*)
327 ((equal name
"token") *token-data-type
*)
330 (defmethod equal-using-type ((type rng-type
) u v
)
333 (defmethod validp ((type rng-type
) e
&optional context
)
334 (declare (ignore e context
))
337 (defmethod type-name ((type string-type
)) "string")
338 (defmethod type-name ((type token-type
)) "token")
340 (defmethod parse ((type string-type
) e
&optional context
)
341 (declare (ignore context
))
344 (defmethod parse ((type token-type
) e
&optional context
)
345 (declare (ignore context
))
346 (normalize-whitespace e
))
348 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
349 (defparameter *whitespace
*
350 (format nil
"~C~C~C~C"
356 (defun normalize-whitespace (str)
357 (cl-ppcre:regex-replace-all
#.
(format nil
"[~A]+" *whitespace
*)
358 (string-trim *whitespace
* str
)
361 (defun replace-whitespace (str)
362 (cl-ppcre:regex-replace-all
#.
(format nil
"[~A]" *whitespace
*)
367 ;;; XML Schema Part 2: Datatypes Second Edition
369 (defparameter *xsd-types
* (make-hash-table :test
'equal
))
372 ((class-name type-name
) (&rest supers
) (&rest slots
) &rest args
)
374 (setf (gethash ,type-name
*xsd-types
*) ',class-name
)
375 (defclass ,class-name
,supers
376 ((type-name :initform
,type-name
382 (defclass xsd-type
(data-type)
383 ((patterns :initform nil
:initarg
:patterns
:reader patterns
))
385 "@short{The class of XML Schema built-in types.}
387 Subclasses of xsd-type provide the built-in types of
388 @a[http://www.w3.org/TR/xmlschema-2/]{
389 XML Schema Part 2: Datatypes Second Edition}
390 as specified in @a[http://relaxng.org/xsd-20010907.html]{Guidelines for
391 using W3C XML Schema Datatypes with RELAX NG}.
394 is named @code{:|http://www.w3.org/2001/XMLSchema-datatypes|}."))
396 (defmethod type-library ((type xsd-type
))
397 :|http
://www.w3.org
/2001/XMLSchema-datatypes|
)
399 (defun zip (keys values
)
400 (loop for key in keys for value in values collect key collect value
))
402 (defgeneric parse-parameter
(class-name type-name param-name value
))
404 (defun parse-parameters (type-class params
)
407 (dolist (param params
(values t patterns args
))
408 (let ((name (param-name param
))
409 (value (param-value param
)))
410 (if (equal name
"pattern")
411 (push value patterns
)
412 (multiple-value-bind (key required-class
)
413 (case (find-symbol (param-name param
) :keyword
)
414 (:|length|
(values :exact-length
'length-mixin
))
415 (:|maxLength|
(values :max-length
'length-mixin
))
416 (:|minLength|
(values :min-length
'length-mixin
))
417 (:|minInclusive|
(values :min-inclusive
'ordering-mixin
))
418 (:|maxInclusive|
(values :max-inclusive
'ordering-mixin
))
419 (:|minExclusive|
(values :min-exclusive
'ordering-mixin
))
420 (:|maxExclusive|
(values :max-exclusive
'ordering-mixin
))
421 (:|totalDigits|
(values :total-digits
'decimal-type
))
422 (:|fractionDigits|
(values :fraction-digits
'decimal-type
))
424 (unless (subtypep type-class required-class
)
427 for
(k nil
) on args by
#'cddr
430 (push (parse-parameter required-class type-class key value
) args
)
431 (push key args
)))))))
434 ((library (eql :|http
://www.w3.org
/2001/XMLSchema-datatypes|
)) name params
)
437 (let ((class (gethash name
*xsd-types
*)))
439 (multiple-value-bind (ok patterns other-args
)
440 (parse-parameters class params
)
442 (apply #'make-instance
449 (defgeneric parse
/xsd
(type e context
))
451 (defgeneric validp
/xsd
(type v context
)
452 (:method-combination and
))
454 (defmethod validp/xsd and
((type xsd-type
) v context
)
455 (declare (ignore context
))
458 (every (lambda (pattern)
459 (cl-ppcre:all-matches pattern v
))
463 (defmethod validp ((type xsd-type
) e
&optional context
)
464 (not (eq :error
(parse/xsd type e context
))))
466 (defmethod parse ((type xsd-type
) e
&optional context
)
467 (let ((result (parse/xsd type e context
)))
468 (when (eq result
:error
)
469 (error "not valid for data type ~A: ~S" type e
))
472 ;; Handle the whiteSpace "facet" before the subclass sees it.
473 ;; If parsing succeded, check other facets by asking validp/xsd.
474 (defmethod parse/xsd
:around
((type xsd-type
) e context
)
475 (let ((result (call-next-method type
476 (munge-whitespace type e
)
478 (if (or (eq result
:error
) (validp/xsd type result context
))
482 (defgeneric munge-whitespace
(type e
))
484 (defmethod munge-whitespace ((type xsd-type
) e
)
485 (normalize-whitespace e
))
490 (defclass ordering-mixin
()
491 ((min-exclusive :initform nil
492 :initarg
:min-exclusive
493 :accessor min-exclusive
)
494 (max-exclusive :initform nil
495 :initarg
:max-exclusive
496 :accessor max-exclusive
)
497 (min-inclusive :initform nil
498 :initarg
:min-inclusive
499 :accessor min-inclusive
)
500 (max-inclusive :initform nil
501 :initarg
:max-inclusive
502 :accessor max-inclusive
)))
504 (defmethod parse-parameter
505 ((class-name (eql 'ordering-mixin
)) type-name
(param t
) value
)
506 (parse (make-instance type-name
) value nil
))
508 (defgeneric lessp-using-type
(type u v
)
510 "@arg[type]{an ordered @class{data-type}}
511 @arg[u]{a parsed value as returned by @fun{parse}}
512 @arg[v]{a parsed value as returned by @fun{parse}}
514 @short{Compare the @emph{values} @code{u} and @code{v} using a
515 data-type-dependent partial ordering.}
517 A method for this function is provided only by types that have a
518 natural partial ordering. The ordering is described in the
519 documentation for the type.
521 @see{equal-using-type}"))
523 (defun <-using-type
(type u v
)
524 (lessp-using-type type u v
))
526 (defun <=-using-type
(type u v
)
527 (or (lessp-using-type type u v
) (equal-using-type type u v
)))
529 ;; it's only a partial ordering, so in general this is not the opposite of <=
530 (defun >-using-type
(type u v
)
531 (lessp-using-type type v u
))
533 ;; it's only a partial ordering, so in general this is not the opposite of <
534 (defun >=-using-type
(type u v
)
535 (or (lessp-using-type type v u
) (equal-using-type type v u
)))
537 (defmethod validp/xsd and
((type ordering-mixin
) v context
)
538 (declare (ignore context
))
539 (with-slots (min-exclusive max-exclusive min-inclusive max-inclusive
) type
540 (and (or (null min-exclusive
) (>-using-type type v min-exclusive
))
541 (or (null max-exclusive
) (<-using-type type v max-exclusive
))
542 (or (null min-inclusive
) (>=-using-type type v min-inclusive
))
543 (or (null max-inclusive
) (<=-using-type type v max-inclusive
)))))
548 (defclass length-mixin
()
549 ((exact-length :initform nil
:initarg
:exact-length
:accessor exact-length
)
550 (min-length :initform nil
:initarg
:min-length
:accessor min-length
)
551 (max-length :initform nil
:initarg
:max-length
:accessor max-length
)))
553 (defmethod parse-parameter
554 ((class-name (eql 'length-mixin
)) (type-name t
) (param t
) value
)
555 (parse (make-instance 'non-negative-integer-type
) value nil
))
557 ;; extra-hack fuer die "Laenge" eines QName...
558 (defgeneric length-using-type
(type u
))
559 (defmethod length-using-type ((type length-mixin
) e
) (length e
))
561 (defmethod validp/xsd and
((type length-mixin
) v context
)
562 (declare (ignore context
))
563 (with-slots (exact-length min-length max-length
) type
564 (or (not (or exact-length min-length max-length
))
565 (let ((l (length-using-type type v
)))
566 (and (or (null exact-length
) (eql l exact-length
))
567 (or (null min-length
) (>= l min-length
))
568 (or (null max-length
) (<= l max-length
)))))))
573 (defclass enumeration-type
(xsd-type length-mixin
)
574 ((word-type :reader word-type
)))
576 (defmethod initialize-instance :after
((type enumeration-type
) &key
)
577 (setf (min-length type
) (max* 1 (min-length type
))))
579 (defmethod parse/xsd
((type enumeration-type
) e context
)
580 (let ((wt (word-type type
)))
582 for word in
(cl-ppcre:split
" " e
)
583 for v
= (parse wt word context
)
585 when
(eq v
:error
) do
(return :error
))))
593 (defxsd (duration-type "duration") (xsd-type ordering-mixin
) ())
595 (defmethod equal-using-type ((type duration-type
) u v
)
598 ;; zzz das ist vielleicht ein bisschen zu woertlich implementiert
599 (defmethod lessp-using-type ((type duration-type
) u v
)
600 (let ((dt (make-instance 'date-time-type
)))
602 (let ((s (parse dt str nil
)))
604 (datetime+duration s u
)
605 (datetime+duration s v
))))
606 '("1696-09-01T00:00:00Z"
607 "1697-02-01T00:00:00Z"
608 "1903-03-01T00:00:00Z"
609 "1903-07-01T00:00:00Z"))))
611 (defun datetime+duration
(s d
)
612 (destructuring-bind (syear smonth sday shour sminute ssecond szone
) s
613 (destructuring-bind (dyear dmonth dday dhour dminute dsecond
) d
614 (labels ((floor3 (a low high
)
615 (multiple-value-bind (u v
)
616 (floor (- a low
) (- high low
))
617 (values u
(+ low v
))))
618 (maximum-day-in-month-for (yearvalue monthvalue
)
619 (multiple-value-bind (m y
)
620 (floor3 monthvalue
1 13)
621 (day-limit m
(+ yearvalue y
)))))
622 (multiple-value-bind (carry emonth
) (floor3 (+ smonth dmonth
) 1 13)
623 (let ((eyear (+ syear dyear carry
))
625 (multiple-value-bind (carry esecond
) (floor (+ ssecond dsecond
) 60)
626 (multiple-value-bind (carry eminute
)
627 (floor (+ sminute dminute carry
) 60)
628 (multiple-value-bind (carry ehour
)
629 (floor (+ shour dhour carry
) 24)
630 (let* ((mdimf (maximum-day-in-month-for eyear emonth
))
631 (tmpdays (max 1 (min sday mdimf
)))
632 (eday (+ tmpdays dday carry
)))
634 (let* ((mdimf (maximum-day-in-month-for eyear emonth
))
638 (setf eday
(+ eday mdimf
))
641 (setf eday
(- eday mdimf
))
645 (tmp (+ emonth carry
)))
646 (multiple-value-bind (y m
)
650 (list eyear emonth eday ehour eminute esecond
653 (defun scan-to-strings (&rest args
)
654 (coerce (nth-value 1 (apply #'cl-ppcre
:scan-to-strings args
)) 'list
))
656 (defmethod parse/xsd
((type duration-type
) e context
)
657 (declare (ignore context
))
658 (destructuring-bind (&optional minusp y m d tp h min s
)
659 (scan-to-strings "(?x)
661 P(?:(\\d+)Y)? # years
662 (?:(\\d+)M)? # months
666 (?:(\\d+)M)? # minutes
667 (?:(\\d+(?:[.]\\d+)?)S)? # seconds
670 (if (and (or y m d h min s
)
671 (or (null tp
) (or h min s
)))
672 (let ((f (if minusp -
1 1)))
674 (and str
(* f
(parse-integer str
)))))
675 (list (int y
) (int m
) (int d
) (int h
) (int min
)
676 (and s
(* f
(parse-number:parse-number s
))))))
682 (defclass time-ordering-mixin
(ordering-mixin) ())
684 (defxsd (date-time-type "dateTime") (xsd-type time-ordering-mixin
) ())
686 (defmethod equal-using-type ((type time-ordering-mixin
) u v
)
689 ;; add zone-offset as a duration (if any), but keep a boolean in the
690 ;; zone-offset field indicating whether there was a time-zone
691 (defun normalize-date-time (u)
692 (destructuring-bind (year month day hour minute second zone-offset
) u
693 (let ((v (list year month day hour minute second
(and zone-offset t
))))
695 (multiple-value-bind (h m
)
696 (truncate zone-offset
)
697 (datetime+timezone v h
(* m
100)))
700 (defun datetime+timezone
(d h m
)
701 (datetime+duration d
(list 0 0 0 h m
0)))
703 (defmethod lessp-using-type ((type time-ordering-mixin
) p q
)
704 (destructuring-bind (pyear pmonth pday phour pminute psecond pzone
)
705 (normalize-date-time p
)
706 (destructuring-bind (qyear qmonth qday qhour qminute qsecond qzone
)
707 (normalize-date-time q
)
709 ((and pzone
(not qzone
))
710 (lessp-using-type type p
(datetime+timezone q
14 0)))
711 ((and (not pzone
) qzone
)
712 (lessp-using-type type
(datetime+timezone p -
14 0) q
))
714 ;; zzz hier sollen wir <> liefern bei Feldern, die in genau einer
715 ;; der Zeiten fehlen. Wir stellen aber fehlende Felder derzeit
716 ;; defaulted dar, koennen diese Situation also nicht feststellen.
717 ;; Einen Unterschied sollte das nur machen, wenn Werte verschiedener
718 ;; Datentypen miteinander verglichen werden. Das bieten wir einfach
721 for a in
(list pyear pmonth pday phour pminute psecond
)
722 for b in
(list qyear qmonth qday qhour qminute qsecond
)
729 (defun day-limit (m y
)
732 (or (zerop (mod y
400))
733 (and (zerop (mod y
4))
734 (not (zerop (mod y
100))))))
740 (defmethod parse-time (minusp y m d h min s tz tz-sign tz-h tz-m
742 (declare (ignore start end
)) ;zzz
743 ;; parse into numbers
745 (and str
(parse-integer str
)))
747 (and str
(parse-number:parse-number str
))))
748 (setf (values y m d h min s tz-h tz-m
)
749 (values (* (int y
) (if minusp -
1 1))
750 (int m
) (int d
) (int h
) (int min
)
752 (int tz-h
) (int tz-m
))))
753 (let ((day-limit (day-limit m y
)))
762 ;; zzz sind leap seconds immer erlaubt?
764 ;; 24:00:00 must be canonicalized
765 (when (and (eql h
24) (zerop min
) (zerop s
))
768 (when (> d day-limit
)
775 (* (if (equal tz-sign
"-") -
1 1)
776 (+ tz-h
(/ tz-m
100))))))
777 (list (* y
(if minusp -
1 1)) m d h min s tz-offset
)
778 ;; (subseq ... start end)
783 (defmethod parse/xsd
((type date-time-type
) e context
)
784 (declare (ignore context
))
785 (destructuring-bind (&optional minusp y m d h min s tz tz-sign tz-h tz-m
)
786 (scan-to-strings "(?x)
788 ((?:[1-9]\\d*)?\\d{4}) # year
794 -(\\d+(?:[.]\\d+)?) # second
795 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
798 (parse-time minusp y m d h min s tz tz-sign tz-h tz-m
)))
803 (defxsd (time-type "time") (xsd-type time-ordering-mixin
) ())
805 (defmethod parse/xsd
((type time-type
) e context
)
806 (declare (ignore context
))
807 (destructuring-bind (&optional h min s tz tz-sign tz-h tz-m
)
808 (scan-to-strings "(?x)
811 -(\\d+(?:[.]\\d+)?) # second
812 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
815 (parse-time nil
1 1 1 h min s tz tz-sign tz-h tz-m
821 (defxsd (date-type "date") (xsd-type time-ordering-mixin
) ())
823 (defmethod parse/xsd
((type date-type
) e context
)
824 (declare (ignore context
))
825 (destructuring-bind (&optional minusp y m d tz tz-sign tz-h tz-m
)
826 (scan-to-strings "(?x)
828 ((?:[1-9]\\d*)?\\d{4}) # year
831 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
834 (parse-time minusp y m d
0 0 0 tz tz-sign tz-h tz-m
840 (defxsd (year-month-type "gYearMonth") (xsd-type time-ordering-mixin
) ())
842 (defmethod parse/xsd
((type year-month-type
) e context
)
843 (declare (ignore context
))
844 (destructuring-bind (&optional minusp y m
)
845 (scan-to-strings "(?x)
847 ((?:[1-9]\\d*)?\\d{4}) # year
851 (parse-time minusp y m
1 0 0 0 nil nil nil nil
857 (defxsd (year-type "gYear") (xsd-type time-ordering-mixin
) ())
859 (defmethod parse/xsd
((type year-month-type
) e context
)
860 (declare (ignore context
))
861 (destructuring-bind (&optional minusp y tz tz-sign tz-h tz-m
)
862 (scan-to-strings "(?x)
864 ((?:[1-9]\\d*)?\\d{4}) # year
865 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
868 (parse-time minusp y
1 1 0 0 0 tz tz-sign tz-h tz-m
874 (defxsd (month-day-type "gMonthDay") (xsd-type time-ordering-mixin
) ())
876 (defmethod parse/xsd
((type month-day-type
) e context
)
877 (declare (ignore context
))
878 (destructuring-bind (&optional m d tz tz-sign tz-h tz-m
)
879 (scan-to-strings "(?x)
882 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
885 (parse-time nil
1 m d
0 0 0 tz tz-sign tz-h tz-m
891 (defxsd (day-type "gDay") (xsd-type time-ordering-mixin
) ())
893 (defmethod parse/xsd
((type day-type
) e context
)
894 (declare (ignore context
))
895 (destructuring-bind (&optional d tz tz-sign tz-h tz-m
)
896 (scan-to-strings "(?x)
898 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
901 (parse-time nil
1 1 d
0 0 0 tz tz-sign tz-h tz-m
907 (defxsd (month-type "gMonth") (xsd-type time-ordering-mixin
) ())
909 (defmethod parse/xsd
((type month-type
) e context
)
910 (declare (ignore context
))
911 (destructuring-bind (&optional m tz tz-sign tz-h tz-m
)
912 (scan-to-strings "(?x)
914 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
917 (parse-time nil
1 m
1 0 0 0 tz tz-sign tz-h tz-m
923 (defxsd (boolean-type "boolean") (xsd-type) ())
925 (defmethod parse/xsd
((type boolean-type
) e context
)
926 (declare (ignore context
))
927 (case (find-symbol e
:keyword
)
929 ((:|false|
:|
0|
) nil
)))
934 (defxsd (base64-binary-type "base64Binary") (xsd-type length-mixin
) ())
936 (defmethod equal-using-type ((type base64-binary-type
) u v
)
939 (defmethod parse/xsd
((type base64-binary-type
) e context
)
940 (declare (ignore context
))
941 (if (cl-ppcre:all-matches
943 ^(([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/]
944 [ ]?[A-Za-z0-9+/][ ]?)*
945 (([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/])
946 | ([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[AEIMQUYcgkosw048][ ]?=)
947 | ([A-Za-z0-9+/][ ]?[AQgw][ ]?=[ ]?=)))?$"
950 (cl-base64:base64-string-to-usb8-array e
)
952 (error "unexpected failure in Base64 decoding: ~A" c
)))
958 (defxsd (hex-binary-type "hexBinary") (xsd-type length-mixin
) ())
960 (defmethod equal-using-type ((type hex-binary-type
) u v
)
963 (defmethod parse/xsd
((type hex-binary-type
) e context
)
964 (declare (ignore context
))
965 (if (evenp (length e
))
967 (make-array (/ (length e
) 2) :element-type
'(unsigned-byte 8))))
969 for i from
0 below
(length e
) by
2
974 (parse-integer e
:start i
:end
(+ i
2) :radix
16)
977 finally
(return result
)))
983 (defxsd (float-type "float") (xsd-type ordering-mixin
) ())
985 (defmethod equal-using-type ((type float-type
) u v
)
988 (defmethod lessp-using-type ((type float-type
) u v
)
991 ;; zzz nehme hier an, dass single-float in IEEE single float ist.
992 ;; Das stimmt unter LispWorks bestimmt wieder nicht.
993 (defmethod parse/xsd
((type float-type
) e context
)
994 (declare (ignore context
))
995 (if (cl-ppcre:all-matches
"^[+-]?\\d+([.]\\d+)?([eE][+-]?\\d+)?$" e
)
996 (coerce (parse-number:parse-number e
) 'single-float
)
1002 (defxsd (decimal-type "decimal") (xsd-type ordering-mixin
)
1003 ((fraction-digits :initform nil
1004 :initarg
:fraction-digits
1005 :accessor fraction-digits
)
1006 (total-digits :initform nil
1007 :initarg
:total-digits
1008 :accessor total-digits
)))
1010 (defmethod parse-parameter
1011 ((class-name (eql 'decimal-type
))
1013 (param (eql :fraction-digits
))
1015 (parse (make-instance 'non-negative-integer-type
) value nil
))
1017 (defmethod parse-parameter
1018 ((class-name (eql 'decimal-type
))
1020 (param (eql :total-digits
))
1022 (parse (make-instance 'positive-integer-type
) value nil
))
1024 (defmethod lessp-using-type ((type decimal-type
) u v
)
1027 (defmethod equal-using-type ((type decimal-type
) u v
)
1030 (defmethod validp/xsd and
((type decimal-type
) v context
)
1031 (declare (ignore context
))
1032 (with-slots (fraction-digits total-digits
) type
1033 (and (or (null fraction-digits
)
1034 (let* ((betrag (abs v
))
1035 (fraction (- betrag
(truncate betrag
)))
1036 (scaled (* fraction
(expt 10 fraction-digits
))))
1037 (zerop (mod scaled
1))))
1038 (or (null total-digits
)
1039 (let ((scaled (abs v
)))
1041 until
(zerop (mod scaled
1))
1042 do
(setf scaled
(* scaled
10)))
1043 (< scaled
(expt 10 total-digits
)))))))
1045 (defmethod parse/xsd
((type decimal-type
) e context
)
1046 (declare (ignore context
))
1047 (destructuring-bind (&optional a b
)
1048 (scan-to-strings "^([+-]?\\d*)(?:[.](\\d+))?$" e
)
1049 (if (plusp (+ (length a
) (length b
)))
1050 (+ (if (plusp (length a
))
1053 (if (plusp (length b
))
1054 (/ (parse-integer b
) (expt 10 (length b
)))
1061 (defxsd (double-type "double") (xsd-type ordering-mixin
) ())
1063 (defmethod equal-using-type ((type double-type
) u v
)
1066 (defmethod lessp-using-type ((type double-type
) u v
)
1069 ;; zzz nehme hier an, dass double-float in IEEE double float ist.
1070 ;; Auch das ist nicht garantiert.
1071 (defmethod parse/xsd
((type double-type
) e context
)
1072 (declare (ignore context
))
1073 (if (cl-ppcre:all-matches
"^[+-]?\\d+([.]\\d+)?([eE][+-]?\\d+)?$" e
)
1074 (coerce (parse-number:parse-number e
) 'double-float
)
1080 (defxsd (any-uri-type "anyURI") (xsd-type length-mixin
) ())
1082 (defmethod equal-using-type ((type any-uri-type
) u v
)
1085 (defmethod parse/xsd
((type any-uri-type
) e context
)
1086 (cxml-rng::escape-uri e
))
1092 (defclass qname-like
(xsd-type length-mixin
) ())
1094 (defxsd (qname-type "QName") (qname-like) ())
1095 (defxsd (notation-type "NOTATION") (qname-like) ())
1097 (defstruct (qname (:constructor make-qname
(uri lname length
)))
1102 (defmethod length-using-type ((type qname-like
) e
)
1105 (defmethod equal-using-type ((type qname-like
) u v
)
1106 (and (equal (qname-uri u
) (qname-uri v
))
1107 (equal (qname-lname u
) (qname-lname v
))))
1110 (and (not (zerop (length str
)))
1111 (cxml::name-start-rune-p
(elt str
0))
1112 (every #'cxml
::name-rune-p str
)))
1114 (defmethod parse/xsd
((type qname-like
) e context
)
1117 (multiple-value-bind (prefix local-name
) (cxml::split-qname e
)
1118 (let ((uri (when prefix
1119 (context-find-namespace-binding context prefix
))))
1120 (if (and prefix
(not uri
))
1122 (make-qname uri local-name
(length e
)))))
1124 (cxml:well-formedness-violation
()
1130 (defxsd (xsd-string-type "string") (xsd-type length-mixin
) ())
1132 (defmethod equal-using-type ((type xsd-string-type
) u v
)
1135 (defmethod munge-whitespace ((type xsd-string-type
) e
)
1138 (defmethod parse/xsd
((type xsd-string-type
) e context
)
1146 ;;; normalizedString
1148 (defxsd (normalized-string-type "normalizedString") (xsd-string-type) ())
1150 (defmethod munge-whitespace ((type normalized-string-type
) e
)
1151 (replace-whitespace e
))
1156 (defxsd (xsd-token-type "token") (normalized-string-type) ())
1158 (defmethod munge-whitespace ((type xsd-token-type
) e
)
1159 (normalize-whitespace e
))
1164 (defxsd (language-type "language") (xsd-token-type)
1165 ((patterns :initform
'("[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"))))
1170 (defxsd (name-type "Name") (xsd-token-type)
1171 ((patterns :initform
'("\\i\\c*"))))
1176 (defxsd (ncname-type "NCName") (name-type)
1177 ((patterns :initform
'("[\\i-[:]][\\c-[:]]*"))))
1179 (defmethod equal-using-type ((type ncname-type
) u v
)
1182 (defun nc-name-p (str)
1183 (and (namep str
) (cxml::nc-name-p str
)))
1185 (defmethod parse/xsd
((type ncname-type
) e context
)
1186 ;; zzz mit pattern machen
1193 (defxsd (id-type "ID") (ncname-type) ())
1198 (defxsd (idref-type "IDREF") (id-type) ())
1203 (defxsd (idrefs-type "IDREFS") (enumeration-type)
1204 ((word-type :initform
(make-instance 'idref-type
))))
1209 (defxsd (entity-type "ENTITY") (ncname-type) ())
1211 (defmethod parse/xsd
((type entity-type
) e context
)
1212 (if (context-find-unparsed-entity context e
)
1219 (defxsd (entities-type "ENTITIES") (enumeration-type)
1220 ((word-type :initform
(make-instance 'entity-type
))))
1225 (defxsd (nmtoken-type "NMTOKEN") (xsd-token-type)
1226 ((patterns :initform
'("\\c+"))))
1231 (defxsd (nmtokens-type "NMTOKENS") (enumeration-type)
1232 ((word-type :initform
(make-instance 'nmtoken-type
))))
1237 (defxsd (integer-type "integer") (decimal-type) ())
1239 ;; period is forbidden, so there's no point in letting decimal handle parsing
1240 ;; fixme: sind fuehrende nullen nun erlaubt oder nicht? die spec sagt ja,
1241 ;; das pattern im schema nicht.
1242 (defmethod parse/xsd
((type integer-type
) e context
)
1243 (declare (ignore context
))
1244 (if (cl-ppcre:all-matches
"^[+-]?(?:[1-9]\\d*|0)$" e
)
1245 (parse-number:parse-number e
)
1249 ;;; nonPositiveInteger
1251 (defxsd (non-positive-integer-type "nonPositiveInteger") (integer-type) ())
1265 (defmethod initialize-instance :after
((type non-positive-integer-type
) &key
)
1266 (setf (max-inclusive type
)
1267 (min* 0 (max-inclusive type
))))
1270 ;;; nonPositiveInteger
1272 (defxsd (negative-integer-type "negativeInteger") (non-positive-integer-type)
1275 (defmethod initialize-instance :after
((type negative-integer-type
) &key
)
1276 (setf (max-inclusive type
)
1277 (min* -
1 (max-inclusive type
))))
1282 (defxsd (long-type "long") (integer-type) ())
1284 (defmethod initialize-instance :after
((type long-type
) &key
)
1285 (setf (max-inclusive type
) (min* 9223372036854775807 (max-inclusive type
)))
1286 (setf (min-inclusive type
) (max* -
9223372036854775808 (min-inclusive type
))))
1291 (defxsd (int-type "int") (long-type) ())
1293 (defmethod initialize-instance :after
((type int-type
) &key
)
1294 (setf (max-inclusive type
) (min* 2147483647 (max-inclusive type
)))
1295 (setf (min-inclusive type
) (max* -
2147483648 (min-inclusive type
))))
1300 (defxsd (short-type "short") (int-type) ())
1302 (defmethod initialize-instance :after
((type short-type
) &key
)
1303 (setf (max-inclusive type
) (min* 32767 (max-inclusive type
)))
1304 (setf (min-inclusive type
) (max* -
32768 (min-inclusive type
))))
1309 (defxsd (byte-type "byte") (short-type) ())
1311 (defmethod initialize-instance :after
((type byte-type
) &key
)
1312 (setf (max-inclusive type
) (min* 127 (max-inclusive type
)))
1313 (setf (min-inclusive type
) (max* -
128 (min-inclusive type
))))
1316 ;;; nonNegativeInteger
1318 (defxsd (non-negative-integer-type "nonNegativeInteger") (integer-type) ())
1320 (defmethod initialize-instance :after
((type non-negative-integer-type
) &key
)
1321 (setf (min-inclusive type
) (max* 0 (min-inclusive type
))))
1326 (defxsd (unsigned-long-type "unsignedLong") (non-negative-integer-type) ())
1328 (defmethod initialize-instance :after
((type unsigned-long-type
) &key
)
1329 (setf (max-inclusive type
) (min* 18446744073709551615 (max-inclusive type
))))
1334 (defxsd (unsigned-int-type "unsignedInt") (unsigned-long-type) ())
1336 (defmethod initialize-instance :after
((type unsigned-int-type
) &key
)
1337 (setf (max-inclusive type
) (min* 4294967295 (max-inclusive type
))))
1342 (defxsd (unsigned-short-type "unsignedShort") (unsigned-int-type) ())
1344 (defmethod initialize-instance :after
((type unsigned-short-type
) &key
)
1345 (setf (max-inclusive type
) (min* 65535 (max-inclusive type
))))
1350 (defxsd (unsigned-byte-type "unsignedByte") (unsigned-short-type) ())
1352 (defmethod initialize-instance :after
((type unsigned-byte-type
) &key
)
1353 (setf (max-inclusive type
) (min* 255 (max-inclusive type
))))
1358 (defxsd (positive-integer-type "positiveInteger") (non-negative-integer-type)
1361 (defmethod initialize-instance :after
((type positive-integer-type
) &key
)
1362 (setf (min-inclusive type
) (max* 1 (min-inclusive type
))))