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 (defmethod print-object ((object rng-type
) stream
)
298 (print-unreadable-object (object stream
:type t
:identity nil
)))
300 (defclass string-type
(rng-type) ()
302 "@short{The Relax NG 'string' type.}
303 This data type allows arbitrary strings and interprets them as-is.
305 For this type, @fun{parse} will return any string unchanged, and
306 @fun{equal-using-type} compares strings using @code{equal}."))
308 (defclass token-type
(rng-type) ()
310 "@short{The Relax NG 'token' type.}
311 This data type allows arbitrary strings and normalizes all whitespaces.
313 For this type, @fun{parse} will return the string with leading and
314 trailing whitespace removed, and remaining sequences of spaces
315 compressed down to one space character each.
317 A method for @fun{equal-using-type} compares strings using @code{equal}."))
319 (defmethod type-library ((type rng-type
))
322 (defvar *string-data-type
* (make-instance 'string-type
))
323 (defvar *token-data-type
* (make-instance 'token-type
))
325 (defmethod find-type ((library (eql :||
)) name params
)
329 ((equal name
"string") *string-data-type
*)
330 ((equal name
"token") *token-data-type
*)
333 (defmethod equal-using-type ((type rng-type
) u v
)
336 (defmethod validp ((type rng-type
) e
&optional context
)
337 (declare (ignore e context
))
340 (defmethod type-name ((type string-type
)) "string")
341 (defmethod type-name ((type token-type
)) "token")
343 (defmethod parse ((type string-type
) e
&optional context
)
344 (declare (ignore context
))
347 (defmethod parse ((type token-type
) e
&optional context
)
348 (declare (ignore context
))
349 (normalize-whitespace e
))
351 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
352 (defparameter *whitespace
*
353 (format nil
"~C~C~C~C"
359 (defun normalize-whitespace (str)
360 (cl-ppcre:regex-replace-all
#.
(format nil
"[~A]+" *whitespace
*)
361 (string-trim *whitespace
* str
)
364 (defun replace-whitespace (str)
365 (cl-ppcre:regex-replace-all
#.
(format nil
"[~A]" *whitespace
*)
370 ;;; XML Schema Part 2: Datatypes Second Edition
372 (defparameter *xsd-types
* (make-hash-table :test
'equal
))
375 ((class-name type-name
) (&rest supers
) (&rest slots
) &rest args
)
377 (setf (gethash ,type-name
*xsd-types
*) ',class-name
)
378 (defclass ,class-name
,supers
379 ((type-name :initform
,type-name
385 (defclass xsd-type
(data-type)
386 ((patterns :initform nil
:initarg
:patterns
:reader patterns
))
388 "@short{The class of XML Schema built-in types.}
390 Subclasses of xsd-type provide the built-in types of
391 @a[http://www.w3.org/TR/xmlschema-2/]{
392 XML Schema Part 2: Datatypes Second Edition}
393 as specified in @a[http://relaxng.org/xsd-20010907.html]{Guidelines for
394 using W3C XML Schema Datatypes with RELAX NG}.
397 is named @code{:|http://www.w3.org/2001/XMLSchema-datatypes|}."))
399 (defmethod print-object ((object xsd-type
) stream
)
400 (print-unreadable-object (object stream
:type t
:identity nil
)
401 (describe-facets object stream
)))
403 (defgeneric describe-facets
(object stream
)
404 (:method-combination progn
))
406 (defmethod describe-facets progn
((object xsd-type
) stream
)
407 (format stream
"~{ :pattern ~A~}" (patterns object
)))
409 (defmethod type-library ((type xsd-type
))
410 :|http
://www.w3.org
/2001/XMLSchema-datatypes|
)
412 (defun zip (keys values
)
413 (loop for key in keys for value in values collect key collect value
))
415 (defgeneric parse-parameter
(class-name type-name param-name value
))
417 (defun parse-parameters (type-class params
)
420 (dolist (param params
(values t patterns args
))
421 (let ((name (param-name param
))
422 (value (param-value param
)))
423 (if (equal name
"pattern")
424 (push value patterns
)
425 (multiple-value-bind (key required-class
)
426 (case (find-symbol (param-name param
) :keyword
)
427 (:|length|
(values :exact-length
'length-mixin
))
428 (:|maxLength|
(values :max-length
'length-mixin
))
429 (:|minLength|
(values :min-length
'length-mixin
))
430 (:|minInclusive|
(values :min-inclusive
'ordering-mixin
))
431 (:|maxInclusive|
(values :max-inclusive
'ordering-mixin
))
432 (:|minExclusive|
(values :min-exclusive
'ordering-mixin
))
433 (:|maxExclusive|
(values :max-exclusive
'ordering-mixin
))
434 (:|totalDigits|
(values :total-digits
'decimal-type
))
435 (:|fractionDigits|
(values :fraction-digits
'decimal-type
))
437 (unless (subtypep type-class required-class
)
440 for
(k nil
) on args by
#'cddr
443 (push (parse-parameter required-class
446 (normalize-whitespace value
))
448 (push key args
)))))))
451 ((library (eql :|http
://www.w3.org
/2001/XMLSchema-datatypes|
)) name params
)
454 (let ((class (gethash name
*xsd-types
*)))
456 (multiple-value-bind (ok patterns other-args
)
457 (parse-parameters class params
)
459 (apply #'make-instance
466 (defgeneric parse
/xsd
(type e context
))
468 (defgeneric validp
/xsd
(type v context
)
469 (:method-combination and
))
471 (defmethod validp/xsd and
((type xsd-type
) v context
)
472 (declare (ignore context
))
475 (every (lambda (pattern)
476 (cl-ppcre:all-matches pattern v
))
480 (defmethod validp ((type xsd-type
) e
&optional context
)
481 (not (eq :error
(parse/xsd type e context
))))
483 (defmethod parse ((type xsd-type
) e
&optional context
)
484 (let ((result (parse/xsd type e context
)))
485 (when (eq result
:error
)
486 (error "not valid for data type ~A: ~S" type e
))
489 ;; Handle the whiteSpace "facet" before the subclass sees it.
490 ;; If parsing succeded, check other facets by asking validp/xsd.
491 (defmethod parse/xsd
:around
((type xsd-type
) e context
)
492 (let ((result (call-next-method type
493 (munge-whitespace type e
)
495 (if (or (eq result
:error
) (validp/xsd type result context
))
499 (defgeneric munge-whitespace
(type e
))
501 (defmethod munge-whitespace ((type xsd-type
) e
)
502 (normalize-whitespace e
))
507 (defclass ordering-mixin
()
508 ((min-exclusive :initform nil
509 :initarg
:min-exclusive
510 :accessor min-exclusive
)
511 (max-exclusive :initform nil
512 :initarg
:max-exclusive
513 :accessor max-exclusive
)
514 (min-inclusive :initform nil
515 :initarg
:min-inclusive
516 :accessor min-inclusive
)
517 (max-inclusive :initform nil
518 :initarg
:max-inclusive
519 :accessor max-inclusive
)))
521 (defmethod describe-facets progn
((object ordering-mixin
) stream
)
522 (dolist (slot '(min-exclusive max-exclusive min-inclusive max-inclusive
))
523 (let ((value (slot-value object slot
)))
525 (format stream
" ~A ~A"
526 (intern (symbol-name slot
) :keyword
)
529 (defmethod parse-parameter
530 ((class-name (eql 'ordering-mixin
)) type-name
(param t
) value
)
531 (parse (make-instance type-name
) value nil
))
533 (defgeneric lessp-using-type
(type u v
)
535 "@arg[type]{an ordered @class{data-type}}
536 @arg[u]{a parsed value as returned by @fun{parse}}
537 @arg[v]{a parsed value as returned by @fun{parse}}
539 @short{Compare the @emph{values} @code{u} and @code{v} using a
540 data-type-dependent partial ordering.}
542 A method for this function is provided only by types that have a
543 natural partial ordering. The ordering is described in the
544 documentation for the type.
546 @see{equal-using-type}"))
548 (defun <-using-type
(type u v
)
549 (lessp-using-type type u v
))
551 (defun <=-using-type
(type u v
)
552 (or (lessp-using-type type u v
) (equal-using-type type u v
)))
554 ;; it's only a partial ordering, so in general this is not the opposite of <=
555 (defun >-using-type
(type u v
)
556 (lessp-using-type type v u
))
558 ;; it's only a partial ordering, so in general this is not the opposite of <
559 (defun >=-using-type
(type u v
)
560 (or (lessp-using-type type v u
) (equal-using-type type v u
)))
562 (defmethod validp/xsd and
((type ordering-mixin
) v context
)
563 (declare (ignore context
))
564 (with-slots (min-exclusive max-exclusive min-inclusive max-inclusive
) type
565 (and (or (null min-exclusive
) (>-using-type type v min-exclusive
))
566 (or (null max-exclusive
) (<-using-type type v max-exclusive
))
567 (or (null min-inclusive
) (>=-using-type type v min-inclusive
))
568 (or (null max-inclusive
) (<=-using-type type v max-inclusive
)))))
573 (defclass length-mixin
()
574 ((exact-length :initform nil
:initarg
:exact-length
:accessor exact-length
)
575 (min-length :initform nil
:initarg
:min-length
:accessor min-length
)
576 (max-length :initform nil
:initarg
:max-length
:accessor max-length
)))
578 (defmethod describe-facets progn
((object length-mixin
) stream
)
579 (dolist (slot '(exact-length min-length max-length
))
580 (let ((value (slot-value object slot
)))
582 (format stream
" ~A ~A"
583 (intern (symbol-name slot
) :keyword
)
586 (defmethod parse-parameter
587 ((class-name (eql 'length-mixin
)) (type-name t
) (param t
) value
)
588 (parse (make-instance 'non-negative-integer-type
) value nil
))
590 ;; extra-hack fuer die "Laenge" eines QName...
591 (defgeneric length-using-type
(type u
))
592 (defmethod length-using-type ((type length-mixin
) e
) (length e
))
594 (defmethod validp/xsd and
((type length-mixin
) v context
)
595 (declare (ignore context
))
596 (with-slots (exact-length min-length max-length
) type
597 (or (not (or exact-length min-length max-length
))
598 (let ((l (length-using-type type v
)))
599 (and (or (null exact-length
) (eql l exact-length
))
600 (or (null min-length
) (>= l min-length
))
601 (or (null max-length
) (<= l max-length
)))))))
606 (defclass enumeration-type
(xsd-type length-mixin
)
607 ((word-type :reader word-type
)))
609 (defmethod initialize-instance :after
((type enumeration-type
) &key
)
610 (setf (min-length type
) (max* 1 (min-length type
))))
612 (defmethod parse/xsd
((type enumeration-type
) e context
)
613 (let ((wt (word-type type
)))
615 for word in
(cl-ppcre:split
" " e
)
616 for v
= (parse wt word context
)
618 when
(eq v
:error
) do
(return :error
))))
626 (defxsd (duration-type "duration") (xsd-type ordering-mixin
) ())
628 (defmethod equal-using-type ((type duration-type
) u v
)
631 ;; zzz das ist vielleicht ein bisschen zu woertlich implementiert
632 (defmethod lessp-using-type ((type duration-type
) u v
)
633 (let ((dt (make-instance 'date-time-type
)))
635 (let ((s (parse dt str nil
)))
637 (datetime+duration s u
)
638 (datetime+duration s v
))))
639 '("1696-09-01T00:00:00Z"
640 "1697-02-01T00:00:00Z"
641 "1903-03-01T00:00:00Z"
642 "1903-07-01T00:00:00Z"))))
644 (defun datetime+duration
(s d
)
645 (destructuring-bind (syear smonth sday shour sminute ssecond szone
) s
646 (destructuring-bind (dyear dmonth dday dhour dminute dsecond
) d
647 (labels ((floor3 (a low high
)
648 (multiple-value-bind (u v
)
649 (floor (- a low
) (- high low
))
650 (values u
(+ low v
))))
651 (maximum-day-in-month-for (yearvalue monthvalue
)
652 (multiple-value-bind (m y
)
653 (floor3 monthvalue
1 13)
654 (day-limit m
(+ yearvalue y
)))))
655 (multiple-value-bind (carry emonth
) (floor3 (+ smonth dmonth
) 1 13)
656 (let ((eyear (+ syear dyear carry
))
658 (multiple-value-bind (carry esecond
) (floor (+ ssecond dsecond
) 60)
659 (multiple-value-bind (carry eminute
)
660 (floor (+ sminute dminute carry
) 60)
661 (multiple-value-bind (carry ehour
)
662 (floor (+ shour dhour carry
) 24)
663 (let* ((mdimf (maximum-day-in-month-for eyear emonth
))
664 (tmpdays (max 1 (min sday mdimf
)))
665 (eday (+ tmpdays dday carry
)))
667 (let* ((mdimf (maximum-day-in-month-for eyear emonth
))
671 (setf eday
(+ eday mdimf
))
674 (setf eday
(- eday mdimf
))
678 (tmp (+ emonth carry
)))
679 (multiple-value-bind (y m
)
683 (list eyear emonth eday ehour eminute esecond
686 (defun scan-to-strings (&rest args
)
687 (coerce (nth-value 1 (apply #'cl-ppcre
:scan-to-strings args
)) 'list
))
689 (defmethod parse/xsd
((type duration-type
) e context
)
690 (declare (ignore context
))
691 (destructuring-bind (&optional minusp y m d tp h min s
)
692 (scan-to-strings "(?x)
694 P(?:(\\d+)Y)? # years
695 (?:(\\d+)M)? # months
699 (?:(\\d+)M)? # minutes
700 (?:(\\d+(?:[.]\\d+)?)S)? # seconds
703 (if (and (or y m d h min s
)
704 (or (null tp
) (or h min s
)))
705 (let ((f (if minusp -
1 1)))
707 (and str
(* f
(parse-integer str
)))))
708 (list (int y
) (int m
) (int d
) (int h
) (int min
)
709 (and s
(* f
(parse-number:parse-number s
))))))
715 (defclass time-ordering-mixin
(ordering-mixin) ())
717 (defxsd (date-time-type "dateTime") (xsd-type time-ordering-mixin
) ())
719 (defmethod equal-using-type ((type time-ordering-mixin
) u v
)
722 ;; add zone-offset as a duration (if any), but keep a boolean in the
723 ;; zone-offset field indicating whether there was a time-zone
724 (defun normalize-date-time (u)
725 (destructuring-bind (year month day hour minute second zone-offset
) u
726 (let ((v (list year month day hour minute second
(and zone-offset t
))))
728 (multiple-value-bind (h m
)
729 (truncate zone-offset
)
730 (datetime+timezone v h
(* m
100)))
733 (defun datetime+timezone
(d h m
)
734 (datetime+duration d
(list 0 0 0 h m
0)))
736 (defmethod lessp-using-type ((type time-ordering-mixin
) p q
)
737 (destructuring-bind (pyear pmonth pday phour pminute psecond pzone
)
738 (normalize-date-time p
)
739 (destructuring-bind (qyear qmonth qday qhour qminute qsecond qzone
)
740 (normalize-date-time q
)
742 ((and pzone
(not qzone
))
743 (lessp-using-type type p
(datetime+timezone q
14 0)))
744 ((and (not pzone
) qzone
)
745 (lessp-using-type type
(datetime+timezone p -
14 0) q
))
747 ;; zzz hier sollen wir <> liefern bei Feldern, die in genau einer
748 ;; der Zeiten fehlen. Wir stellen aber fehlende Felder derzeit
749 ;; defaulted dar, koennen diese Situation also nicht feststellen.
750 ;; Einen Unterschied sollte das nur machen, wenn Werte verschiedener
751 ;; Datentypen miteinander verglichen werden. Das bieten wir einfach
754 for a in
(list pyear pmonth pday phour pminute psecond
)
755 for b in
(list qyear qmonth qday qhour qminute qsecond
)
762 (defun day-limit (m y
)
765 (or (zerop (mod y
400))
766 (and (zerop (mod y
4))
767 (not (zerop (mod y
100))))))
773 (defmethod parse-time (minusp y m d h min s tz tz-sign tz-h tz-m
775 (declare (ignore start end
)) ;zzz
776 ;; parse into numbers
778 (and str
(parse-integer str
)))
780 (and str
(parse-number:parse-number str
))))
781 (setf (values y m d h min s tz-h tz-m
)
782 (values (* (int y
) (if minusp -
1 1))
783 (int m
) (int d
) (int h
) (int min
)
785 (int tz-h
) (int tz-m
))))
786 (let ((day-limit (day-limit m y
)))
795 ;; zzz sind leap seconds immer erlaubt?
797 ;; 24:00:00 must be canonicalized
798 (when (and (eql h
24) (zerop min
) (zerop s
))
801 (when (> d day-limit
)
808 (* (if (equal tz-sign
"-") -
1 1)
809 (+ tz-h
(/ tz-m
100))))))
810 (list (* y
(if minusp -
1 1)) m d h min s tz-offset
)
811 ;; (subseq ... start end)
816 (defmethod parse/xsd
((type date-time-type
) e context
)
817 (declare (ignore context
))
818 (destructuring-bind (&optional minusp y m d h min s tz tz-sign tz-h tz-m
)
819 (scan-to-strings "(?x)
821 ((?:[1-9]\\d*)?\\d{4}) # year
827 :(\\d+(?:[.]\\d+)?) # second
828 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
831 (parse-time minusp y m d h min s tz tz-sign tz-h tz-m
)))
836 (defxsd (time-type "time") (xsd-type time-ordering-mixin
) ())
838 (defmethod parse/xsd
((type time-type
) e context
)
839 (declare (ignore context
))
840 (destructuring-bind (&optional h min s tz tz-sign tz-h tz-m
)
841 (scan-to-strings "(?x)
844 :(\\d+(?:[.]\\d+)?) # second
845 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
848 (parse-time nil
"1" "1" "1" h min s tz tz-sign tz-h tz-m
854 (defxsd (date-type "date") (xsd-type time-ordering-mixin
) ())
856 (defmethod parse/xsd
((type date-type
) e context
)
857 (declare (ignore context
))
858 (destructuring-bind (&optional minusp y m d tz tz-sign tz-h tz-m
)
859 (scan-to-strings "(?x)
861 ((?:[1-9]\\d*)?\\d{4}) # year
864 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
867 (parse-time minusp y m d
"0" "0" "0" tz tz-sign tz-h tz-m
873 (defxsd (year-month-type "gYearMonth") (xsd-type time-ordering-mixin
) ())
875 (defmethod parse/xsd
((type year-month-type
) e context
)
876 (declare (ignore context
))
877 (destructuring-bind (&optional minusp y m
)
878 (scan-to-strings "(?x)
880 ((?:[1-9]\\d*)?\\d{4}) # year
884 (parse-time minusp y m
"1" "0" "0" "0" nil nil nil nil
890 (defxsd (year-type "gYear") (xsd-type time-ordering-mixin
) ())
892 (defmethod parse/xsd
((type year-type
) e context
)
893 (declare (ignore context
))
894 (destructuring-bind (&optional minusp y tz tz-sign tz-h tz-m
)
895 (scan-to-strings "(?x)
897 ((?:[1-9]\\d*)?\\d{4}) # year
898 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
901 (parse-time minusp y
"1" "1" "0" "0" "0" tz tz-sign tz-h tz-m
907 (defxsd (month-day-type "gMonthDay") (xsd-type time-ordering-mixin
) ())
909 (defmethod parse/xsd
((type month-day-type
) e context
)
910 (declare (ignore context
))
911 (destructuring-bind (&optional m d tz tz-sign tz-h tz-m
)
912 (scan-to-strings "(?x)
915 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
918 (parse-time nil
"1" m d
"0" "0" "0" tz tz-sign tz-h tz-m
924 (defxsd (day-type "gDay") (xsd-type time-ordering-mixin
) ())
926 (defmethod parse/xsd
((type day-type
) e context
)
927 (declare (ignore context
))
928 (destructuring-bind (&optional d tz tz-sign tz-h tz-m
)
929 (scan-to-strings "(?x)
931 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
934 (parse-time nil
"1" "1" d
"0" "0" "0" tz tz-sign tz-h tz-m
940 (defxsd (month-type "gMonth") (xsd-type time-ordering-mixin
) ())
942 (defmethod parse/xsd
((type month-type
) e context
)
943 (declare (ignore context
))
944 (destructuring-bind (&optional m tz tz-sign tz-h tz-m
)
945 (scan-to-strings "(?x)
947 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
950 (parse-time nil
"1" m
"1" "0" "0" "0" tz tz-sign tz-h tz-m
956 (defxsd (boolean-type "boolean") (xsd-type) ())
958 (defmethod parse/xsd
((type boolean-type
) e context
)
959 (declare (ignore context
))
960 (case (find-symbol e
:keyword
)
962 ((:|false|
:|
0|
) nil
)))
967 (defxsd (base64-binary-type "base64Binary") (xsd-type length-mixin
) ())
969 (defmethod equal-using-type ((type base64-binary-type
) u v
)
972 (defmethod parse/xsd
((type base64-binary-type
) e context
)
973 (declare (ignore context
))
974 (if (cl-ppcre:all-matches
976 ^(([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/]
977 [ ]?[A-Za-z0-9+/][ ]?)*
978 (([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/])
979 | ([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[AEIMQUYcgkosw048][ ]?=)
980 | ([A-Za-z0-9+/][ ]?[AQgw][ ]?=[ ]?=)))?$"
983 (cl-base64:base64-string-to-usb8-array e
)
985 (error "unexpected failure in Base64 decoding: ~A" c
)))
991 (defxsd (hex-binary-type "hexBinary") (xsd-type length-mixin
) ())
993 (defmethod equal-using-type ((type hex-binary-type
) u v
)
996 (defmethod parse/xsd
((type hex-binary-type
) e context
)
997 (declare (ignore context
))
998 (if (evenp (length e
))
1000 (make-array (/ (length e
) 2) :element-type
'(unsigned-byte 8))))
1002 for i from
0 below
(length e
) by
2
1005 (setf (elt result j
)
1007 (parse-integer e
:start i
:end
(+ i
2) :radix
16)
1010 finally
(return result
)))
1016 (defxsd (float-type "float") (xsd-type ordering-mixin
) ())
1018 (defmethod equal-using-type ((type float-type
) u v
)
1019 #+(or sbcl allegro
) (= u v
)
1020 #-
(or sbcl allegro
) (float= u v
))
1022 (defmethod lessp-using-type ((type float-type
) u v
)
1023 #+(or sbcl allegro
) (< u v
)
1024 #-
(or sbcl allegro
) (float< u v
))
1026 ;; this one is more complex than would seem necessary, because too-large
1027 ;; and too-small values must be rounded to infinity rather than erroring out
1028 (defun parse-float (e min max
+inf -inf nan
)
1030 ((equal e
"INF") +inf
)
1031 ((equal e
"-INF") -inf
)
1032 ((equal e
"Nan") nan
)
1034 (destructuring-bind (&optional a b
)
1035 (scan-to-strings "^([^eE]+)(?:[eE]([^eE]+))?$" e
)
1037 (let* ((mantissa (parse/xsd
(make-instance 'decimal-type
) a nil
))
1040 (parse/xsd
(make-instance 'integer-type
) b nil
))))
1041 (if (or (eq mantissa
:error
) (eq exponent
:error
))
1043 (let ((ratio (* mantissa
(expt 10 (or exponent
1)))))
1045 ((< ratio min
) -inf
)
1046 ((> ratio max
) +inf
)
1047 (t (float ratio min
))))))
1050 ;; zzz nehme hier an, dass single-float in IEEE single float ist.
1051 ;; Das stimmt unter LispWorks bestimmt wieder nicht.
1052 (defmethod parse/xsd
((type float-type
) e context
)
1053 (declare (ignore context
))
1055 most-negative-single-float
1056 most-positive-single-float
1057 single-float-positive-infinity
1058 single-float-negative-infinity
1064 (defxsd (decimal-type "decimal") (xsd-type ordering-mixin
)
1065 ((fraction-digits :initform nil
1066 :initarg
:fraction-digits
1067 :accessor fraction-digits
)
1068 (total-digits :initform nil
1069 :initarg
:total-digits
1070 :accessor total-digits
)))
1072 (defmethod describe-facets progn
((object decimal-type
) stream
)
1073 (dolist (slot '(fraction-digits total-digits
))
1074 (let ((value (slot-value object slot
)))
1076 (format stream
" ~A ~A"
1077 (intern (symbol-name slot
) :keyword
)
1080 (defmethod parse-parameter
1081 ((class-name (eql 'decimal-type
))
1083 (param (eql :fraction-digits
))
1085 (parse (make-instance 'non-negative-integer-type
) value nil
))
1087 (defmethod parse-parameter
1088 ((class-name (eql 'decimal-type
))
1090 (param (eql :total-digits
))
1092 (parse (make-instance 'positive-integer-type
) value nil
))
1094 (defmethod lessp-using-type ((type decimal-type
) u v
)
1097 (defmethod equal-using-type ((type decimal-type
) u v
)
1100 (defmethod validp/xsd and
((type decimal-type
) v context
)
1101 (declare (ignore context
))
1102 (with-slots (fraction-digits total-digits
) type
1103 (and (or (null fraction-digits
)
1104 (let* ((betrag (abs v
))
1105 (fraction (- betrag
(truncate betrag
)))
1106 (scaled (* fraction
(expt 10 fraction-digits
))))
1107 (zerop (mod scaled
1))))
1108 (or (null total-digits
)
1109 (let ((scaled (abs v
)))
1111 until
(zerop (mod scaled
1))
1112 do
(setf scaled
(* scaled
10)))
1113 (< scaled
(expt 10 total-digits
)))))))
1115 (defmethod parse/xsd
((type decimal-type
) e context
)
1116 (declare (ignore context
))
1117 (destructuring-bind (&optional a b
)
1118 (scan-to-strings "^([+-]?\\d*)(?:[.](\\d+))?$" e
)
1119 (if (plusp (+ (length a
) (length b
)))
1120 (+ (if (plusp (length a
))
1123 (if (plusp (length b
))
1124 (/ (parse-integer b
) (expt 10 (length b
)))
1131 (defxsd (double-type "double") (xsd-type ordering-mixin
) ())
1133 (defmethod equal-using-type ((type double-type
) u v
)
1134 #+(or sbcl allegro
) (= u v
)
1135 #-
(or sbcl allegro
) (float= u v
))
1137 (defmethod lessp-using-type ((type double-type
) u v
)
1138 #+(or sbcl allegro
) (< u v
)
1139 #-
(or sbcl allegro
) (float< u v
))
1141 ;; zzz nehme hier an, dass double-float in IEEE double float ist.
1142 ;; Auch das ist nicht garantiert.
1143 (defmethod parse/xsd
((type double-type
) e context
)
1144 (declare (ignore context
))
1146 most-negative-double-float
1147 most-positive-double-float
1148 double-float-positive-infinity
1149 double-float-negative-infinity
1155 (defxsd (any-uri-type "anyURI") (xsd-type length-mixin
) ())
1157 (defmethod equal-using-type ((type any-uri-type
) u v
)
1160 (defmethod parse/xsd
((type any-uri-type
) e context
)
1161 (cxml-rng::escape-uri e
))
1167 (defclass qname-like
(xsd-type length-mixin
) ())
1169 (defxsd (qname-type "QName") (qname-like) ())
1170 (defxsd (notation-type "NOTATION") (qname-like) ())
1172 (defstruct (qname (:constructor make-qname
(uri lname length
)))
1177 (defmethod length-using-type ((type qname-like
) e
)
1180 (defmethod equal-using-type ((type qname-like
) u v
)
1181 (and (equal (qname-uri u
) (qname-uri v
))
1182 (equal (qname-lname u
) (qname-lname v
))))
1185 (and (not (zerop (length str
)))
1186 (cxml::name-start-rune-p
(elt str
0))
1187 (every #'cxml
::name-rune-p str
)))
1189 (defmethod parse/xsd
((type qname-like
) e context
)
1192 (multiple-value-bind (prefix local-name
) (cxml::split-qname e
)
1193 (let ((uri (when prefix
1194 (context-find-namespace-binding context prefix
))))
1195 (if (and prefix
(not uri
))
1197 (make-qname uri local-name
(length e
)))))
1199 (cxml:well-formedness-violation
()
1205 (defxsd (xsd-string-type "string") (xsd-type length-mixin
) ())
1207 (defmethod equal-using-type ((type xsd-string-type
) u v
)
1210 (defmethod munge-whitespace ((type xsd-string-type
) e
)
1213 (defmethod parse/xsd
((type xsd-string-type
) e context
)
1221 ;;; normalizedString
1223 (defxsd (normalized-string-type "normalizedString") (xsd-string-type) ())
1225 (defmethod munge-whitespace ((type normalized-string-type
) e
)
1226 (replace-whitespace e
))
1231 (defxsd (xsd-token-type "token") (normalized-string-type) ())
1233 (defmethod munge-whitespace ((type xsd-token-type
) e
)
1234 (normalize-whitespace e
))
1239 (defxsd (language-type "language") (xsd-token-type)
1240 ((patterns :initform
'("[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"))))
1245 (defxsd (name-type "Name") (xsd-token-type)
1246 ((patterns :initform
'("\\i\\c*"))))
1251 (defxsd (ncname-type "NCName") (name-type)
1252 ((patterns :initform
'("[\\i-[:]][\\c-[:]]*"))))
1254 (defmethod equal-using-type ((type ncname-type
) u v
)
1257 (defun nc-name-p (str)
1258 (and (namep str
) (cxml::nc-name-p str
)))
1260 (defmethod parse/xsd
((type ncname-type
) e context
)
1261 ;; zzz mit pattern machen
1268 (defxsd (id-type "ID") (ncname-type) ())
1273 (defxsd (idref-type "IDREF") (id-type) ())
1278 (defxsd (idrefs-type "IDREFS") (enumeration-type)
1279 ((word-type :initform
(make-instance 'idref-type
))))
1284 (defxsd (entity-type "ENTITY") (ncname-type) ())
1286 (defmethod parse/xsd
((type entity-type
) e context
)
1287 (if (context-find-unparsed-entity context e
)
1294 (defxsd (entities-type "ENTITIES") (enumeration-type)
1295 ((word-type :initform
(make-instance 'entity-type
))))
1300 (defxsd (nmtoken-type "NMTOKEN") (xsd-token-type)
1301 ((patterns :initform
'("\\c+"))))
1306 (defxsd (nmtokens-type "NMTOKENS") (enumeration-type)
1307 ((word-type :initform
(make-instance 'nmtoken-type
))))
1312 (defxsd (integer-type "integer") (decimal-type) ())
1314 ;; period is forbidden, so there's no point in letting decimal handle parsing
1315 ;; fixme: sind fuehrende nullen nun erlaubt oder nicht? die spec sagt ja,
1316 ;; das pattern im schema nicht.
1317 (defmethod parse/xsd
((type integer-type
) e context
)
1318 (declare (ignore context
))
1319 (if (cl-ppcre:all-matches
"^[+-]?(?:[1-9]\\d*|0)$" e
)
1320 (parse-number:parse-number e
)
1324 ;;; nonPositiveInteger
1326 (defxsd (non-positive-integer-type "nonPositiveInteger") (integer-type) ())
1340 (defmethod initialize-instance :after
((type non-positive-integer-type
) &key
)
1341 (setf (max-inclusive type
)
1342 (min* 0 (max-inclusive type
))))
1345 ;;; nonPositiveInteger
1347 (defxsd (negative-integer-type "negativeInteger") (non-positive-integer-type)
1350 (defmethod initialize-instance :after
((type negative-integer-type
) &key
)
1351 (setf (max-inclusive type
)
1352 (min* -
1 (max-inclusive type
))))
1357 (defxsd (long-type "long") (integer-type) ())
1359 (defmethod initialize-instance :after
((type long-type
) &key
)
1360 (setf (max-inclusive type
) (min* 9223372036854775807 (max-inclusive type
)))
1361 (setf (min-inclusive type
) (max* -
9223372036854775808 (min-inclusive type
))))
1366 (defxsd (int-type "int") (long-type) ())
1368 (defmethod initialize-instance :after
((type int-type
) &key
)
1369 (setf (max-inclusive type
) (min* 2147483647 (max-inclusive type
)))
1370 (setf (min-inclusive type
) (max* -
2147483648 (min-inclusive type
))))
1375 (defxsd (short-type "short") (int-type) ())
1377 (defmethod initialize-instance :after
((type short-type
) &key
)
1378 (setf (max-inclusive type
) (min* 32767 (max-inclusive type
)))
1379 (setf (min-inclusive type
) (max* -
32768 (min-inclusive type
))))
1384 (defxsd (byte-type "byte") (short-type) ())
1386 (defmethod initialize-instance :after
((type byte-type
) &key
)
1387 (setf (max-inclusive type
) (min* 127 (max-inclusive type
)))
1388 (setf (min-inclusive type
) (max* -
128 (min-inclusive type
))))
1391 ;;; nonNegativeInteger
1393 (defxsd (non-negative-integer-type "nonNegativeInteger") (integer-type) ())
1395 (defmethod initialize-instance :after
((type non-negative-integer-type
) &key
)
1396 (setf (min-inclusive type
) (max* 0 (min-inclusive type
))))
1401 (defxsd (unsigned-long-type "unsignedLong") (non-negative-integer-type) ())
1403 (defmethod initialize-instance :after
((type unsigned-long-type
) &key
)
1404 (setf (max-inclusive type
) (min* 18446744073709551615 (max-inclusive type
))))
1409 (defxsd (unsigned-int-type "unsignedInt") (unsigned-long-type) ())
1411 (defmethod initialize-instance :after
((type unsigned-int-type
) &key
)
1412 (setf (max-inclusive type
) (min* 4294967295 (max-inclusive type
))))
1417 (defxsd (unsigned-short-type "unsignedShort") (unsigned-int-type) ())
1419 (defmethod initialize-instance :after
((type unsigned-short-type
) &key
)
1420 (setf (max-inclusive type
) (min* 65535 (max-inclusive type
))))
1425 (defxsd (unsigned-byte-type "unsignedByte") (unsigned-short-type) ())
1427 (defmethod initialize-instance :after
((type unsigned-byte-type
) &key
)
1428 (setf (max-inclusive type
) (min* 255 (max-inclusive type
))))
1433 (defxsd (positive-integer-type "positiveInteger") (non-negative-integer-type)
1436 (defmethod initialize-instance :after
((type positive-integer-type
) &key
)
1437 (setf (min-inclusive type
) (max* 1 (min-inclusive type
))))