patterns lueppen
[cxml-rng.git] / types.lisp
blobd73c933b708af6d4ef268553ff1f535c0b4d0ed0
1 ;;; -*- show-trailing-whitespace: t; indent-tabs: nil -*-
2 ;;;
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
7 ;;; are met:
8 ;;;
9 ;;; * Redistributions of source code must retain the above copyright
10 ;;; notice, this list of conditions and the following disclaimer.
11 ;;;
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.
16 ;;;
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}
36 @see{find-type}
37 @see{cxml-rng:pattern-params}
38 @see{cxml-rng:data}
39 @see-slot{param-name}
40 @see-slot{param-value}"
41 name
42 value)
44 (setf (documentation 'make-param 'function)
45 "@arg[name]{parameter name, a string}
46 @arg[value]{parameter value, a string}
47 @return{a @class{param}}
48 Create a data type parameter.
49 @see{param-name}
50 @see{param-value}")
52 (setf (documentation 'param-name 'function)
53 "@arg[instance]{an instance of @class{param}}
54 @return{a string}
55 The data type parameter's name.
56 @see{param-value}")
58 (setf (documentation 'param-value 'function)
59 "@arg[instance]{an instance of @class{param}}
60 @return{a string}
61 The data type parameter's value.
62 @see{param-name}")
64 (defclass data-type () ()
65 (:documentation
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}
72 @see-slot{type-name}
73 @see-slot{type-library}
74 @see-slot{type-context-dependent-p}
75 @see{parse}
76 @see{equal-using-type}
77 @see{lessp-using-type}
78 @see{validp}"))
80 (defgeneric find-type (library name params)
81 (:documentation
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 Additional parameters (knows as restricting facets in XSD) can be passed
89 to specify or restrict the type for the purposes of @fun{validp}.
91 Return a type instance for this type and the additional parameters,
92 @code{nil} if the type does not exist, or
93 @code{:error} if the type exists, but the specified parameters are not
94 valid for that type.
96 @see{data-type}"))
98 (defgeneric type-library (type)
99 (:documentation
100 "@arg[type]{an instance of @class{data-type}}
101 @return{library name, a keyword}
102 @short{Return the name of the library this type belongs to.}
104 @see{type-name}
105 @see{type-context-dependent-p}"))
107 (defgeneric type-name (type)
108 (:documentation
109 "@arg[type]{an instance of @class{data-type}}
110 @return{type name, a string}
111 @short{Return the name this type has within its library.}
113 @see{type-library}
114 @see{type-context-dependent-p}"))
116 (defmethod find-type ((library t) name params)
117 (declare (ignore name params))
118 nil)
120 (defgeneric type-context-dependent-p (type)
121 (:documentation
122 "@arg[type]{an instance of @class{data-type}}
123 @return{a boolean}
124 @short{Return true if parsing and validation of values by this type
125 depends on the validation context.}
127 In this case, the optional @code{context} argument to @fun{parse} and
128 @fun{validp} is required, and an error will be signalled if it is missing.
130 @see{validation-context}
131 @see{type-name}
132 @see{type-library}
133 @see{type-context-dependent-p}"))
135 (defmethod type-context-dependent-p ((type data-type))
136 nil)
138 (defgeneric equal-using-type (type u v)
139 (:documentation
140 "@arg[type]{an instance of @class{data-type}}
141 @arg[u]{a parsed value as returned by @fun{parse}}
142 @arg[v]{a parsed value as returned by @fun{parse}}
143 @return{a boolean}
144 @short{Compare the @emph{values} @code{u} and @code{v} using a
145 data-type-dependent equality function.}
147 @see{validp}"))
149 (defgeneric parse (type e &optional context)
150 (:documentation
151 "@arg[type]{an instance of @class{data-type}}
152 @arg[e]{a string}
153 @arg[context]{an instance of @class{validation-context}}
154 @return{an object}
155 @short{Parse string @code{e} and return a representation of its value
156 as defined by the data type.}
158 The @code{context} argument is required if @fun{type-context-dependent-p}
159 is true for @code{type}, and will be ignored otherwise.
161 @see{equal-using-type}
162 @see{validp}"))
164 (defgeneric validp (type e &optional context)
165 (:documentation
166 "@arg[type]{an instance of @class{data-type}}
167 @arg[e]{a string}
168 @arg[context]{an instance of @class{validation-context}}
169 @return{a boolean}
170 @short{Determine whether a string is a valid lexical representation
171 for a type.}
173 The @code{context} argument is required if @fun{type-context-dependent-p}
174 is true for @code{type}, and will be ignored otherwise.
176 @see{parse}
177 @see{equal-using-type}"))
180 ;;; Validation context
182 (defclass validation-context () ()
183 (:documentation
184 "@short{This abstract class defines a protocol allowing data types
185 to query the XML parser about its current state.}
187 Some types are context dependent, as indicated by
188 @fun{type-context-dependent-p}. Those types need access to state
189 computed by the XML parser implicitly, like namespace bindings or
190 the Base URI.
192 User-defined subclasses must implement methods
193 for the functions @fun{context-find-namespace-binding} and
194 @fun{context-find-unparsed-entity}.
196 Two pre-defined validation context implementations are
197 provided, one for use with SAX, the other based on Klacks."))
199 (defgeneric context-find-namespace-binding (context prefix)
200 (:documentation
201 "@arg[context]{an instance of @class{validation-context}}
202 @arg[prefix]{name prefix, a string}
203 @return{the namespace URI as a string, or NIL}
204 @short{This function resolves a namespace prefix to a namespace URI in the
205 current context.}
206 All currently declared namespaces
207 are taken into account, including those declared directly on the
208 current element."))
210 (defgeneric context-find-unparsed-entity (context name)
211 (:documentation
212 "@arg[context]{an instance of @class{validation-context}}
213 @arg[name]{entity name, a string}
214 @return{@code{nil}, or a list of public id, system id, and notation name}
215 This function looks for an unparsed entity in the current context."))
217 (defclass klacks-validation-context (validation-context)
218 ((source :initarg :source :accessor context-source))
219 (:documentation
220 "A validation-context implementation that queries
221 a klacks source for information about the parser's current state.
222 @see-constructor{make-klacks-validation-context}"))
224 (defun make-klacks-validation-context (source)
225 "@arg[source]{a @a[http://common-lisp.net/project/cxml/klacks.html]{
226 klacks source}}
227 @return{a @class{klacks-validation-context}}
228 Create a validation-context that will query the given klacks source for
229 the current parser context."
230 (make-instance 'klacks-validation-context :source source))
232 (defmethod context-find-namespace-binding
233 ((context klacks-validation-context) prefix)
234 (klacks:find-namespace-binding prefix (context-source context)))
236 ;; zzz nicht schoen.
237 (defmethod context-find-unparsed-entity
238 ((context klacks-validation-context) name)
239 (or (dolist (x (slot-value (context-source context)
240 'cxml::external-declarations))
241 (when (and (eq (car x) 'sax:unparsed-entity-declaration)
242 (equal (cadr x) name))
243 (return t)))
244 (dolist (x (slot-value (context-source context)
245 'cxml::internal-declarations))
246 (when (and (eq (car x) 'sax:unparsed-entity-declaration)
247 (equal (cadr x) name))
248 (return t)))))
250 (defclass sax-validation-context-mixin (validation-context)
251 ((stack :initform nil :accessor context-stack)
252 (unparsed-entities :initform (make-hash-table :test 'equal)
253 :accessor unparsed-entities))
254 (:documentation
255 "@short{A class that implements validation-context as a mixin for
256 user-defined SAX handler classes.}
258 The mixin will record namespace information
259 automatically, and the user's SAX handler can simply be passed as a
260 validation context to data type functions."))
262 (defmethod sax:start-prefix-mapping
263 ((handler sax-validation-context-mixin) prefix uri)
264 (push (cons prefix uri) (context-stack handler)))
266 (defmethod sax:end-prefix-mapping
267 ((handler sax-validation-context-mixin) prefix)
268 (setf (context-stack handler)
269 (remove prefix
270 (context-stack handler)
271 :count 1
272 :key #'car
273 :test #'equal)))
275 (defmethod sax:unparsed-entity-declaration
276 ((context sax-validation-context-mixin)
277 name public-id system-id notation-name)
278 (setf (gethash name (unparsed-entities context))
279 (list public-id system-id notation-name)))
281 (defmethod context-find-namespace-binding
282 ((context sax-validation-context-mixin) prefix)
283 (cdr (assoc prefix (context-stack context) :test #'equal)))
285 (defmethod context-find-unparsed-entity
286 ((context sax-validation-context-mixin) name)
287 (gethash name (unparsed-entities context)))
290 ;;; Relax NG built-in type library
292 (defclass rng-type (data-type) ()
293 (:documentation
294 "@short{The class of Relax NG built-in types.}
295 Relax NG defines two built-in data type: string and token.
297 The Relax NG type library is named @code{:||}."))
299 (defmethod print-object ((object rng-type) stream)
300 (print-unreadable-object (object stream :type t :identity nil)))
302 (defclass string-type (rng-type) ()
303 (:documentation
304 "@short{The Relax NG 'string' type.}
305 This data type allows arbitrary strings and interprets them as-is.
307 For this type, @fun{parse} will return any string unchanged, and
308 @fun{equal-using-type} compares strings using @code{equal}."))
310 (defclass token-type (rng-type) ()
311 (:documentation
312 "@short{The Relax NG 'token' type.}
313 This data type allows arbitrary strings and normalizes all whitespaces.
315 For this type, @fun{parse} will return the string with leading and
316 trailing whitespace removed, and remaining sequences of spaces
317 compressed down to one space character each.
319 A method for @fun{equal-using-type} compares strings using @code{equal}."))
321 (defmethod type-library ((type rng-type))
322 :||)
324 (defvar *string-data-type* (make-instance 'string-type))
325 (defvar *token-data-type* (make-instance 'token-type))
327 (defmethod find-type ((library (eql :||)) name params)
328 (cond
329 ((eq name :probe) t)
330 (params :error)
331 ((equal name "string") *string-data-type*)
332 ((equal name "token") *token-data-type*)
333 (t nil)))
335 (defmethod equal-using-type ((type rng-type) u v)
336 (equal u v))
338 (defmethod validp ((type rng-type) e &optional context)
339 (declare (ignore e context))
342 (defmethod type-name ((type string-type)) "string")
343 (defmethod type-name ((type token-type)) "token")
345 (defmethod parse ((type string-type) e &optional context)
346 (declare (ignore context))
349 (defmethod parse ((type token-type) e &optional context)
350 (declare (ignore context))
351 (normalize-whitespace e))
353 (eval-when (:compile-toplevel :load-toplevel :execute)
354 (defparameter *whitespace*
355 (format nil "~C~C~C~C"
356 (code-char 9)
357 (code-char 32)
358 (code-char 13)
359 (code-char 10))))
361 (defun normalize-whitespace (str)
362 (cl-ppcre:regex-replace-all #.(format nil "[~A]+" *whitespace*)
363 (string-trim *whitespace* str)
364 " "))
366 (defun replace-whitespace (str)
367 (cl-ppcre:regex-replace-all #.(format nil "[~A]" *whitespace*)
369 " "))
372 ;;; XML Schema Part 2: Datatypes Second Edition
374 (defparameter *xsd-types* (make-hash-table :test 'equal))
376 (defmacro defxsd
377 ((class-name type-name) (&rest supers) (&rest slots) &rest args)
378 `(progn
379 (setf (gethash ,type-name *xsd-types*) ',class-name)
380 (defclass ,class-name ,supers
381 ((type-name :initform ,type-name
382 :reader type-name
383 :allocation :class)
384 ,@slots)
385 ,@args)))
387 (defgeneric patterns (data-type)
388 (:documentation
389 "@arg[data-type]{a subtype of @class{xsd-type}}
390 @return{a list of strings}
391 This slot reader returns a list of the type's
392 @a[http://www.w3.org/TR/xmlschema-2/#rf-pattern]{pattern facets}."))
394 (defmethod (setf patterns) :after (newval data-type)
395 (slot-makunbound data-type 'compiled-patterns))
397 (defclass xsd-type (data-type)
398 ((patterns :initform nil :accessor patterns)
399 (compiled-patterns :accessor compiled-patterns))
400 (:documentation
401 "@short{The class of XML Schema built-in types.}
403 Subclasses of xsd-type provide the built-in types of
404 @a[http://www.w3.org/TR/xmlschema-2/]{
405 XML Schema Part 2: Datatypes Second Edition}
406 as specified in @a[http://relaxng.org/xsd-20010907.html]{Guidelines for
407 using W3C XML Schema Datatypes with RELAX NG}.
409 The XSD type library
410 is named @code{:|http://www.w3.org/2001/XMLSchema-datatypes|}.
412 @b{Parameters.} All XSD types accept regular expressions restricting
413 the set of strings accepted by the type. The pattern parameter is
414 called @code{\"pattern\"}. This parameter can be repeated to specify
415 multiple regular expressions that must all match the data.
416 As an initarg, specify @code{:pattern} with a list of regular expressions
417 as an argument.
419 @see-slot{patterns}"))
421 (defmethod initialize-instance :after ((instance xsd-type) &key patterns)
422 (setf (patterns instance) (append (patterns instance) patterns)))
424 (defmethod print-object ((object xsd-type) stream)
425 (print-unreadable-object (object stream :type t :identity nil)
426 (describe-facets object stream)))
428 (defgeneric describe-facets (object stream)
429 (:method-combination progn))
431 (defmethod describe-facets progn ((object xsd-type) stream)
432 (format stream "~{ :pattern ~A~}" (patterns object)))
434 (defmethod type-library ((type xsd-type))
435 :|http://www.w3.org/2001/XMLSchema-datatypes|)
437 (defun zip (keys values)
438 (loop for key in keys for value in values collect key collect value))
440 (defgeneric parse-parameter (class-name type-name param-name value))
442 (defun parse-parameters (type-class params)
443 (let ((patterns '())
444 (args '()))
445 (dolist (param params (values t patterns args))
446 (let ((name (param-name param))
447 (value (param-value param)))
448 (if (equal name "pattern")
449 (push value patterns)
450 (multiple-value-bind (key required-class)
451 (case (find-symbol (param-name param) :keyword)
452 (:|length| (values :exact-length 'length-mixin))
453 (:|maxLength| (values :max-length 'length-mixin))
454 (:|minLength| (values :min-length 'length-mixin))
455 (:|minInclusive| (values :min-inclusive 'ordering-mixin))
456 (:|maxInclusive| (values :max-inclusive 'ordering-mixin))
457 (:|minExclusive| (values :min-exclusive 'ordering-mixin))
458 (:|maxExclusive| (values :max-exclusive 'ordering-mixin))
459 (:|totalDigits| (values :total-digits 'decimal-type))
460 (:|fractionDigits| (values :fraction-digits 'decimal-type))
461 (t (return nil)))
462 (unless (subtypep type-class required-class)
463 (return nil))
464 (when (loop
465 for (k nil) on args by #'cddr
466 thereis (eq key k))
467 (return nil))
468 (push (parse-parameter required-class
469 type-class
471 (normalize-whitespace value))
472 args)
473 (push key args)))))))
475 (defmethod find-type
476 ((library (eql :|http://www.w3.org/2001/XMLSchema-datatypes|)) name params)
477 (if (eq name :probe)
479 (let ((class (gethash name *xsd-types*)))
480 (if class
481 (multiple-value-bind (ok patterns other-args)
482 (parse-parameters class params)
483 (if ok
484 (apply #'make-instance
485 class
486 :patterns patterns
487 other-args)
488 :error))
489 nil))))
491 (defgeneric parse/xsd (type e context))
493 (defgeneric validp/xsd (type v context)
494 (:method-combination and))
496 (defmethod validp/xsd and ((type xsd-type) v context)
497 (declare (ignore context))
498 (unless (slot-boundp type 'compiled-patterns)
499 (setf (compiled-patterns type)
500 (mapcar #'pattern-scanner (patterns type))))
501 (every (lambda (pattern)
502 (cl-ppcre:all-matches pattern v))
503 (compiled-patterns type)))
505 (defmethod validp ((type xsd-type) e &optional context)
506 (not (eq :error (parse/xsd type e context))))
508 (defmethod parse ((type xsd-type) e &optional context)
509 (let ((result (parse/xsd type e context)))
510 (when (eq result :error)
511 (error "not valid for data type ~A: ~S" type e))
512 result))
514 ;; Handle the whiteSpace "facet" before the subclass sees it.
515 ;; If parsing succeded, check other facets by asking validp/xsd.
516 (defmethod parse/xsd :around ((type xsd-type) e context)
517 (let ((result (call-next-method type
518 (munge-whitespace type e)
519 context)))
520 (if (or (eq result :error) (validp/xsd type result context))
521 result
522 :error)))
524 (defgeneric munge-whitespace (type e))
526 (defmethod munge-whitespace ((type xsd-type) e)
527 (normalize-whitespace e))
530 ;;; ordering-mixin
532 (defgeneric min-exclusive (data-type)
533 (:documentation
534 "@arg[data-type]{an ordered data type}
535 @return{an integer, or @code{nil}}
536 This slot reader returns the type's
537 @a[http://www.w3.org/TR/xmlschema-2/#rf-minExclusive]{minExclusive facet},
538 or @code{nil} if none was specified.
539 @see{max-exclusive}
540 @see{min-inclusive}
541 @see{max-inclusive}"))
543 (defgeneric max-exclusive (data-type)
544 (:documentation
545 "@arg[data-type]{an ordered data type}
546 @return{an integer, or @code{nil}}
547 This slot reader returns the type's
548 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive]{maxExclusive facet},
549 or @code{nil} if none was specified.
550 @see{min-exclusive}
551 @see{min-inclusive}
552 @see{max-inclusive}"))
554 (defgeneric min-inclusive (data-type)
555 (:documentation
556 "@arg[data-type]{an ordered data type}
557 @return{an integer, or @code{nil}}
558 This slot reader returns the type's
559 @a[http://www.w3.org/TR/xmlschema-2/#rf-minInclusive]{minInclusive facet},
560 or @code{nil} if none was specified.
561 @see{min-exclusive}
562 @see{max-exclusive}
563 @see{max-inclusive}"))
565 (defgeneric max-inclusive (data-type)
566 (:documentation
567 "@arg[data-type]{an ordered data type}
568 @return{an integer, or @code{nil}}
569 This slot reader returns the type's
570 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive]{maxInclusive facet},
571 or @code{nil} if none was specified.
572 @see{min-exclusive}
573 @see{max-exclusive}
574 @see{min-inclusive}"))
576 (defclass ordering-mixin ()
577 ((min-exclusive :initform nil
578 :initarg :min-exclusive
579 :accessor min-exclusive)
580 (max-exclusive :initform nil
581 :initarg :max-exclusive
582 :accessor max-exclusive)
583 (min-inclusive :initform nil
584 :initarg :min-inclusive
585 :accessor min-inclusive)
586 (max-inclusive :initform nil
587 :initarg :max-inclusive
588 :accessor max-inclusive)))
590 (defmethod describe-facets progn ((object ordering-mixin) stream)
591 (dolist (slot '(min-exclusive max-exclusive min-inclusive max-inclusive))
592 (let ((value (slot-value object slot)))
593 (when value
594 (format stream " ~A ~A"
595 (intern (symbol-name slot) :keyword)
596 value)))))
598 (defmethod parse-parameter
599 ((class-name (eql 'ordering-mixin)) type-name (param t) value)
600 (parse (make-instance type-name) value nil))
602 (defgeneric lessp-using-type (type u v)
603 (:documentation
604 "@arg[type]{an ordered @class{data-type}}
605 @arg[u]{a parsed value as returned by @fun{parse}}
606 @arg[v]{a parsed value as returned by @fun{parse}}
607 @return{a boolean}
608 @short{Compare the @emph{values} @code{u} and @code{v} using a
609 data-type-dependent partial ordering.}
611 A method for this function is provided only by types that have a
612 natural partial ordering.
614 @see{equal-using-type}"))
616 (defun <-using-type (type u v)
617 (lessp-using-type type u v))
619 (defun <=-using-type (type u v)
620 (or (lessp-using-type type u v) (equal-using-type type u v)))
622 ;; it's only a partial ordering, so in general this is not the opposite of <=
623 (defun >-using-type (type u v)
624 (lessp-using-type type v u))
626 ;; it's only a partial ordering, so in general this is not the opposite of <
627 (defun >=-using-type (type u v)
628 (or (lessp-using-type type v u) (equal-using-type type v u)))
630 (defmethod validp/xsd and ((type ordering-mixin) v context)
631 (declare (ignore context))
632 (with-slots (min-exclusive max-exclusive min-inclusive max-inclusive) type
633 (and (or (null min-exclusive) (>-using-type type v min-exclusive))
634 (or (null max-exclusive) (<-using-type type v max-exclusive))
635 (or (null min-inclusive) (>=-using-type type v min-inclusive))
636 (or (null max-inclusive) (<=-using-type type v max-inclusive)))))
639 ;;; length-mixin
641 (defgeneric exact-length (data-type)
642 (:documentation
643 "@arg[data-type]{a data type supporting restrictions on value lengths}
644 @return{an integer, or @code{nil}}
645 This slot reader returns the type's
646 @a[http://www.w3.org/TR/xmlschema-2/#rf-length]{length facet},
647 or @code{nil} if none was specified.
648 @see{min-length}
649 @see{max-length}"))
651 (defgeneric min-length (data-type)
652 (:documentation
653 "@arg[data-type]{a data type supporting restrictions on value lengths}
654 @return{an integer, or @code{nil}}
655 This slot reader returns the type's
656 @a[http://www.w3.org/TR/xmlschema-2/#rf-minLength]{minLength facet},
657 or @code{nil} if none was specified.
658 @see{exact-length}
659 @see{max-length}"))
661 (defgeneric max-length (data-type)
662 (:documentation
663 "@arg[data-type]{a data type supporting restrictions on value lengths}
664 @return{an integer, or @code{nil}}
665 This slot reader returns the type's
666 @a[http://www.w3.org/TR/xmlschema-2/#rf-maxLength]{maxLength facet},
667 or @code{nil} if none was specified.
668 @see{exact-length}
669 @see{min-length}"))
671 (defclass length-mixin ()
672 ((exact-length :initform nil :initarg :exact-length :accessor exact-length)
673 (min-length :initform nil :initarg :min-length :accessor min-length)
674 (max-length :initform nil :initarg :max-length :accessor max-length)))
676 (defmethod describe-facets progn ((object length-mixin) stream)
677 (dolist (slot '(exact-length min-length max-length))
678 (let ((value (slot-value object slot)))
679 (when value
680 (format stream " ~A ~A"
681 (intern (symbol-name slot) :keyword)
682 value)))))
684 (defmethod parse-parameter
685 ((class-name (eql 'length-mixin)) (type-name t) (param t) value)
686 (parse (make-instance 'non-negative-integer-type) value nil))
688 ;; extra-hack fuer die "Laenge" eines QName...
689 (defgeneric length-using-type (type u))
690 (defmethod length-using-type ((type length-mixin) e) (length e))
692 (defmethod validp/xsd and ((type length-mixin) v context)
693 (declare (ignore context))
694 (with-slots (exact-length min-length max-length) type
695 (or (not (or exact-length min-length max-length))
696 (let ((l (length-using-type type v)))
697 (and (or (null exact-length) (eql l exact-length))
698 (or (null min-length) (>= l min-length))
699 (or (null max-length) (<= l max-length)))))))
702 ;;; enumeration-type
704 (defclass enumeration-type (xsd-type length-mixin)
705 ((word-type :reader word-type)))
707 (defmethod initialize-instance :after ((type enumeration-type) &key)
708 (setf (min-length type) (max* 1 (min-length type))))
710 (defmethod parse/xsd ((type enumeration-type) e context)
711 (let ((wt (word-type type)))
712 (loop
713 for word in (cl-ppcre:split " " e)
714 for v = (parse wt word context)
715 collect v
716 when (eq v :error) do (return :error))))
720 ;;;; Primitive types
722 ;;; duration
724 (defxsd (duration-type "duration") (xsd-type ordering-mixin)
726 (:documentation
727 "@short{The duration data type, representing a duration of time.}
729 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
730 the @a[http://www.w3.org/TR/xmlschema-2/#duration]{specification}.
732 @b{Implementation.} This type returns lists of the form
733 @code{(years months days hours minutes seconds)}. Each
734 value can be @code{nil} or a number. All values are integers
735 except for @code{seconds}, which is a real.
737 @b{Example.} @code{P1Y2M3DT10H30M}
738 maps to @code{(1 2 3 10 30 nil)}
740 @b{Parameters.} This type is ordered and allows the parameters
741 @slot{max-inclusive}, @slot{min-inclusive},
742 @slot{max-exclusive}, and @slot{min-exclusive}."))
744 (defmethod equal-using-type ((type duration-type) u v)
745 (equal u v))
747 ;; zzz das ist vielleicht ein bisschen zu woertlich implementiert
748 (defmethod lessp-using-type ((type duration-type) u v)
749 (let ((dt (make-instance 'date-time-type)))
750 (every (lambda (str)
751 (let ((s (parse dt str nil)))
752 (lessp-using-type dt
753 (datetime+duration s u)
754 (datetime+duration s v))))
755 '("1696-09-01T00:00:00Z"
756 "1697-02-01T00:00:00Z"
757 "1903-03-01T00:00:00Z"
758 "1903-07-01T00:00:00Z"))))
760 (defun datetime+duration (s d)
761 (destructuring-bind (syear smonth sday shour sminute ssecond szone) s
762 (destructuring-bind (dyear dmonth dday dhour dminute dsecond) d
763 (setf dhour (or dhour 0))
764 (setf dminute (or dminute 0))
765 (setf dsecond (or dsecond 0))
766 (labels ((floor3 (a low high)
767 (multiple-value-bind (u v)
768 (floor (- a low) (- high low))
769 (values u (+ low v))))
770 (maximum-day-in-month-for (yearvalue monthvalue)
771 (multiple-value-bind (m y)
772 (floor3 monthvalue 1 13)
773 (day-limit m (+ yearvalue y)))))
774 (multiple-value-bind (carry emonth) (floor3 (+ smonth dmonth) 1 13)
775 (let ((eyear (+ syear dyear carry))
776 (ezone szone))
777 (multiple-value-bind (carry esecond) (floor (+ ssecond dsecond) 60)
778 (multiple-value-bind (carry eminute)
779 (floor (+ sminute dminute carry) 60)
780 (multiple-value-bind (carry ehour)
781 (floor (+ shour dhour carry) 24)
782 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
783 (tmpdays (max 1 (min sday mdimf)))
784 (eday (+ tmpdays dday carry)))
785 (loop
786 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
787 (carry
788 (cond
789 ((< eday 1)
790 (setf eday (+ eday mdimf))
792 ((> eday mdimf)
793 (setf eday (- eday mdimf))
796 (return))))
797 (tmp (+ emonth carry)))
798 (multiple-value-bind (y m)
799 (floor3 tmp 1 13)
800 (setf emonth m)
801 (incf eyear y))))
802 (list eyear emonth eday ehour eminute esecond
803 ezone)))))))))))
805 (defun scan-to-strings (&rest args)
806 (coerce (nth-value 1 (apply #'cl-ppcre:scan-to-strings args)) 'list))
808 (defmethod parse/xsd ((type duration-type) e context)
809 (declare (ignore context))
810 (destructuring-bind (&optional minusp y m d tp h min s)
811 (scan-to-strings "(?x)
812 ^(-)? # minus
813 P(?:(\\d+)Y)? # years
814 (?:(\\d+)M)? # months
815 (?:(\\d+)D)? # days
816 (T # (time)
817 (?:(\\d+)H)? # hours
818 (?:(\\d+)M)? # minutes
819 (?:(\\d+(?:[.]\\d+)?)S)? # seconds
820 )?$"
822 (if (and (or y m d h min s)
823 (or (null tp) (or h min s)))
824 (let ((f (if minusp -1 1)))
825 (flet ((int (str)
826 (and str (* f (parse-integer str)))))
827 (list (int y) (int m) (int d) (int h) (int min)
828 (and s (* f (parse-number:parse-number s))))))
829 :error)))
832 ;;; dateTime
834 (defclass time-ordering-mixin (ordering-mixin) ())
836 (defxsd (date-time-type "dateTime") (xsd-type time-ordering-mixin)
838 (:documentation
839 "@short{The dateTime data type, representing a moment in time.}
841 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
842 the @a[http://www.w3.org/TR/xmlschema-2/#dateTime]{specification}.
844 @b{Implementation.} This type returns lists of the form
845 @code{(year month day hour minute second timezone)}. Each
846 value is an integer, except except for @code{second}, which is a real,
847 and @code{timezone} which is a real or @code{nil}.
848 A @code{timezone} of @code{nil} indicates UTC.
850 @b{Example.} @code{2002-10-10T12:00:00-05:00}
851 maps to @code{(2002 10 10 12 0 0 -5)}
853 @b{Parameters.} This type is ordered and allows the parameters
854 @slot{max-inclusive}, @slot{min-inclusive},
855 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
856 except within a timezone, see the spec for details."))
858 (defmethod equal-using-type ((type time-ordering-mixin) u v)
859 (equal u v))
861 ;; add zone-offset as a duration (if any), but keep a boolean in the
862 ;; zone-offset field indicating whether there was a time-zone
863 (defun normalize-date-time (u)
864 (destructuring-bind (year month day hour minute second zone-offset) u
865 (let ((v (list year month day hour minute second (and zone-offset t))))
866 (if zone-offset
867 (multiple-value-bind (h m)
868 (truncate zone-offset)
869 (datetime+timezone v h (* m 100)))
870 v))))
872 (defun datetime+timezone (d h m)
873 (datetime+duration d (list 0 0 0 h m 0)))
875 (defmethod lessp-using-type ((type time-ordering-mixin) p q)
876 (destructuring-bind (pyear pmonth pday phour pminute psecond pzone)
877 (normalize-date-time p)
878 (destructuring-bind (qyear qmonth qday qhour qminute qsecond qzone)
879 (normalize-date-time q)
880 (cond
881 ((and pzone (not qzone))
882 (lessp-using-type type p (datetime+timezone q 14 0)))
883 ((and (not pzone) qzone)
884 (lessp-using-type type (datetime+timezone p -14 0) q))
886 ;; zzz hier sollen wir <> liefern bei Feldern, die in genau einer
887 ;; der Zeiten fehlen. Wir stellen aber fehlende Felder derzeit
888 ;; defaulted dar, koennen diese Situation also nicht feststellen.
889 ;; Einen Unterschied sollte das nur machen, wenn Werte verschiedener
890 ;; Datentypen miteinander verglichen werden. Das bieten wir einfach
891 ;; nicht an.
892 (loop
893 for a in (list pyear pmonth pday phour pminute psecond)
894 for b in (list qyear qmonth qday qhour qminute qsecond)
896 (when (< a b)
897 (return t))
898 (when (> a b)
899 (return nil))))))))
901 (defun day-limit (m y)
902 (cond
903 ((and (eql m 2)
904 (or (zerop (mod y 400))
905 (and (zerop (mod y 4))
906 (not (zerop (mod y 100))))))
908 ((eql m 2) 28)
909 ((if (<= m 7) (oddp m) (evenp m)) 31)
910 (t 30)))
912 (defun parse-time (minusp y m d h min s tz tz-sign tz-h tz-m
913 &key (start 0) end)
914 (declare (ignore tz start end)) ;zzz
915 ;; parse into numbers
916 (flet ((int (str)
917 (and str (parse-integer str)))
918 (num (str)
919 (and str (parse-number:parse-number str))))
920 (setf (values y m d h min s tz-h tz-m)
921 (values (* (int y) (if minusp -1 1))
922 (int m) (int d) (int h) (int min)
923 (num s)
924 (int tz-h) (int tz-m))))
925 (let ((day-limit (day-limit m y)))
926 ;; check ranges
927 (cond
928 ((and y m d h min s
929 (plusp y)
930 (<= 1 m 12)
931 (<= 1 d day-limit)
932 (<= 0 h 24)
933 (<= 0 min 59)
934 ;; zzz sind leap seconds immer erlaubt?
935 (<= 0 s 60))
936 ;; 24:00:00 must be canonicalized
937 (when (and (eql h 24) (zerop min) (zerop s))
938 (incf h)
939 (incf d)
940 (when (> d day-limit)
941 (setf d 1)
942 (incf m)
943 (when (> m 12)
944 (incf y))))
945 (let ((tz-offset
946 (when tz-h
947 (* (if (equal tz-sign "-") -1 1)
948 (+ tz-h (/ tz-m 100))))))
949 (list (* y (if minusp -1 1)) m d h min s tz-offset)
950 ;; (subseq ... start end)
953 :error))))
955 (defmethod parse/xsd ((type date-time-type) e context)
956 (declare (ignore context))
957 (destructuring-bind (&optional minusp y m d h min s tz tz-sign tz-h tz-m)
958 (scan-to-strings "(?x)
959 ^(-)? # opt. minus
960 ((?:[1-9]\\d*)?\\d{4}) # year
961 -(\\d\\d) # month
962 -(\\d\\d) # day
963 T # (time)
964 (\\d\\d) # hour
965 :(\\d\\d) # minute
966 :(\\d+(?:[.]\\d+)?) # second
967 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
970 (parse-time minusp y m d h min s tz tz-sign tz-h tz-m)))
973 ;;; time
975 (defxsd (time-type "time") (xsd-type time-ordering-mixin)
977 (:documentation
978 "@short{The time data type, representing a time of day.}
980 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
981 the @a[http://www.w3.org/TR/xmlschema-2/#dateTime]{specification}.
983 @b{Implementation.} This type returns the same kind of lists as
984 @class{date-time-type}, except that the fields @code{year},
985 @code{month} and @code{day} are filled with dummy values from the
986 Gregorian year AD 1.
988 @b{Parameters.} This type is ordered and allows the parameters
989 @slot{max-inclusive}, @slot{min-inclusive},
990 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
991 except within a timezone, see the spec for details."))
993 (defmethod parse/xsd ((type time-type) e context)
994 (declare (ignore context))
995 (destructuring-bind (&optional h min s tz tz-sign tz-h tz-m)
996 (scan-to-strings "(?x)
997 ^(\\d\\d) # hour
998 :(\\d\\d) # minute
999 :(\\d+(?:[.]\\d+)?) # second
1000 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1003 (parse-time nil "1" "1" "1" h min s tz tz-sign tz-h tz-m
1004 :start 3)))
1007 ;;; date
1009 (defxsd (date-type "date") (xsd-type time-ordering-mixin)
1011 (:documentation
1012 "@short{The date data type, representing a day of the year.}
1014 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1015 the @a[http://www.w3.org/TR/xmlschema-2/#date]{specification}.
1017 @b{Implementation.} This type returns the same kind of lists as
1018 @class{date-time-type}, except that the fields @code{hour},
1019 @code{minute} and @code{second} are filled with dummy values from the
1020 Gregorian year AD 1.
1022 @b{Parameters.} This type is ordered and allows the parameters
1023 @slot{max-inclusive}, @slot{min-inclusive},
1024 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1025 except within a timezone, see the spec for details."))
1027 (defmethod parse/xsd ((type date-type) e context)
1028 (declare (ignore context))
1029 (destructuring-bind (&optional minusp y m d tz tz-sign tz-h tz-m)
1030 (scan-to-strings "(?x)
1031 ^(-)? # opt. minus
1032 ((?:[1-9]\\d*)?\\d{4}) # year
1033 -(\\d\\d) # month
1034 -(\\d\\d) # day
1035 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1038 (parse-time minusp y m d "0" "0" "0" tz tz-sign tz-h tz-m
1039 :end 3)))
1042 ;;; gYearMonth
1044 (defxsd (year-month-type "gYearMonth") (xsd-type time-ordering-mixin)
1046 (:documentation
1047 "@short{The gYearMonth data type, representing the calendar month of a
1048 specific year.}
1050 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1051 the @a[http://www.w3.org/TR/xmlschema-2/#gYearMonth]{specification}.
1053 @b{Implementation.} This type returns the same kind of lists as
1054 @class{date-time-type}, except that the fields @code{day}, @code{hour},
1055 @code{minute} and @code{second} are filled with dummy values from the
1056 Gregorian year AD 1.
1058 @b{Parameters.} This type is ordered and allows the parameters
1059 @slot{max-inclusive}, @slot{min-inclusive},
1060 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1061 except within a timezone, see the spec for details."))
1063 (defmethod parse/xsd ((type year-month-type) e context)
1064 (declare (ignore context))
1065 (destructuring-bind (&optional minusp y m)
1066 (scan-to-strings "(?x)
1067 ^(-)? # opt. minus
1068 ((?:[1-9]\\d*)?\\d{4}) # year
1069 -(\\d\\d) # month
1072 (parse-time minusp y m "1" "0" "0" "0" nil nil nil nil
1073 :end 2)))
1076 ;;; gYear
1078 (defxsd (year-type "gYear") (xsd-type time-ordering-mixin)
1080 (:documentation
1081 "@short{The gYear data type, representing a calendar year.}
1083 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1084 the @a[http://www.w3.org/TR/xmlschema-2/#gYear]{specification}.
1086 @b{Implementation.} This type returns the same kind of lists as
1087 @class{date-time-type}, except that the fields @code{month}, @code{day},
1088 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1089 from the Gregorian year AD 1.
1091 @b{Parameters.} This type is ordered and allows the parameters
1092 @slot{max-inclusive}, @slot{min-inclusive},
1093 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1094 except within a timezone, see the spec for details."))
1096 (defmethod parse/xsd ((type year-type) e context)
1097 (declare (ignore context))
1098 (destructuring-bind (&optional minusp y tz tz-sign tz-h tz-m)
1099 (scan-to-strings "(?x)
1100 ^(-)? # opt. minus
1101 ((?:[1-9]\\d*)?\\d{4}) # year
1102 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1105 (parse-time minusp y "1" "1" "0" "0" "0" tz tz-sign tz-h tz-m
1106 :end 1)))
1109 ;;; gMonthDay
1111 (defxsd (month-day-type "gMonthDay") (xsd-type time-ordering-mixin)
1113 (:documentation
1114 "@short{The gMonthDay data type, representing a calendar month and day.}
1116 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1117 the @a[http://www.w3.org/TR/xmlschema-2/#monthDay]{specification}.
1119 @b{Implementation.} This type returns the same kind of lists as
1120 @class{date-time-type}, except that the fields @code{year},
1121 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1122 from the Gregorian year AD 1.
1124 @b{Parameters.} This type is ordered and allows the parameters
1125 @slot{max-inclusive}, @slot{min-inclusive},
1126 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1127 except within a timezone, see the spec for details."))
1129 (defmethod parse/xsd ((type month-day-type) e context)
1130 (declare (ignore context))
1131 (destructuring-bind (&optional m d tz tz-sign tz-h tz-m)
1132 (scan-to-strings "(?x)
1133 ^--(\\d\\d) # month
1134 -(\\d\\d) # day
1135 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1138 (parse-time nil "1" m d "0" "0" "0" tz tz-sign tz-h tz-m
1139 :start 1 :end 3)))
1142 ;;; gDay
1144 (defxsd (day-type "gDay") (xsd-type time-ordering-mixin)
1146 (:documentation
1147 "@short{The gDay data type, representing a calendar day.}
1149 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1150 the @a[http://www.w3.org/TR/xmlschema-2/#gDay]{specification}.
1152 @b{Implementation.} This type returns the same kind of lists as
1153 @class{date-time-type}, except that the fields @code{year}, @code{month},
1154 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1155 from the Gregorian year AD 1.
1157 @b{Parameters.} This type is ordered and allows the parameters
1158 @slot{max-inclusive}, @slot{min-inclusive},
1159 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1160 except within a timezone, see the spec for details."))
1162 (defmethod parse/xsd ((type day-type) e context)
1163 (declare (ignore context))
1164 (destructuring-bind (&optional d tz tz-sign tz-h tz-m)
1165 (scan-to-strings "(?x)
1166 ---(\\d\\d) # day
1167 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1170 (parse-time nil "1" "1" d "0" "0" "0" tz tz-sign tz-h tz-m
1171 :start 3 :end 4)))
1174 ;;; gMonth
1176 (defxsd (month-type "gMonth") (xsd-type time-ordering-mixin)
1178 (:documentation
1179 "@short{The gMonth data type, representing a calendar month.}
1181 @b{Syntax.} This type accepts an ISO-like syntax. For details refer to
1182 the @a[http://www.w3.org/TR/xmlschema-2/#gMonth]{specification}.
1184 @b{Implementation.} This type returns the same kind of lists as
1185 @class{date-time-type}, except that the fields @code{year}, @code{day},
1186 @code{hour}, @code{minute} and @code{second} are filled with dummy values
1187 from the Gregorian year AD 1.
1189 @b{Parameters.} This type is ordered and allows the parameters
1190 @slot{max-inclusive}, @slot{min-inclusive},
1191 @slot{max-exclusive}, and @slot{min-exclusive}. The ordering is partial
1192 except within a timezone, see the spec for details."))
1194 (defmethod parse/xsd ((type month-type) e context)
1195 (declare (ignore context))
1196 (destructuring-bind (&optional m tz tz-sign tz-h tz-m)
1197 (scan-to-strings "(?x)
1198 ^--(\\d\\d) # month
1199 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
1202 (parse-time nil "1" m "1" "0" "0" "0" tz tz-sign tz-h tz-m
1203 :start 2 :end 3)))
1206 ;;; boolean
1208 (defxsd (boolean-type "boolean") (xsd-type)
1210 (:documentation
1211 "@short{The boolean data type.}
1213 @b{Syntax.} \"1\", \"0\", \"true\", or \"false\".
1214 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#boolean]{specification}.
1216 @b{Implementation.} This type returns @code{t} or @code{nil}.
1218 @b{Parameters.} No parameters except for @fun{pattern} are available for
1219 this type."))
1221 (defmethod parse/xsd ((type boolean-type) e context)
1222 (declare (ignore context))
1223 (case (find-symbol e :keyword)
1224 ((:|true| :|1|) t)
1225 ((:|false| :|0|) nil)))
1228 ;;; base64Binary
1230 (defxsd (base64-binary-type "base64Binary") (xsd-type length-mixin)
1232 (:documentation
1233 "@short{The base64Binary data type.}
1235 @b{Syntax.} Normal Base64 syntax.
1236 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#base64Binary]{specification}.
1238 @b{Implementation.} This type returns an @code{(unsigned-byte 8)}
1239 vector.
1241 @b{Parameters.} This type allows restrictions on the length of the octet
1242 vector through the parameters @slot{exact-length}, @slot{min-length}, and
1243 @slot{max-length}."))
1245 (defmethod equal-using-type ((type base64-binary-type) u v)
1246 (equalp u v))
1248 (defmethod parse/xsd ((type base64-binary-type) e context)
1249 (declare (ignore context))
1250 (if (cl-ppcre:all-matches
1251 "(?x)
1252 ^(([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/]
1253 [ ]?[A-Za-z0-9+/][ ]?)*
1254 (([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/])
1255 | ([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[AEIMQUYcgkosw048][ ]?=)
1256 | ([A-Za-z0-9+/][ ]?[AQgw][ ]?=[ ]?=)))?$"
1258 (handler-case
1259 (cl-base64:base64-string-to-usb8-array e)
1260 (warning (c)
1261 (error "unexpected failure in Base64 decoding: ~A" c)))
1262 :error))
1265 ;;; hexBinary
1267 (defxsd (hex-binary-type "hexBinary") (xsd-type length-mixin)
1269 (:documentation
1270 "@short{The hexBinary data type.}
1272 @b{Syntax.} A sequence of two-digit hexadecimal numbers representing
1273 one octet each.
1274 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#hexBinary]{specification}.
1276 @b{Implementation.} This type returns an @code{(unsigned-byte 8)}
1277 vector.
1279 @b{Parameters.} This type allows restrictions on the length of the octet
1280 vector through the parameters @slot{exact-length}, @slot{min-length}, and
1281 @slot{max-length}."))
1283 (defmethod equal-using-type ((type hex-binary-type) u v)
1284 (equalp u v))
1286 (defmethod parse/xsd ((type hex-binary-type) e context)
1287 (declare (ignore context))
1288 (if (evenp (length e))
1289 (let ((result
1290 (make-array (/ (length e) 2) :element-type '(unsigned-byte 8))))
1291 (loop
1292 for i from 0 below (length e) by 2
1293 for j from 0
1295 (setf (elt result j)
1296 (handler-case
1297 (parse-integer e :start i :end (+ i 2) :radix 16)
1298 (error ()
1299 (return :error))))
1300 finally (return result)))
1301 :error))
1304 ;;; float
1306 (defxsd (float-type "float") (xsd-type ordering-mixin)
1308 (:documentation
1309 "@short{The float data type.}
1311 @b{Syntax.} A floating-point number in a \"scientific notation\".
1312 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#float]{specification}.
1314 @b{Implementation.} This type returns a @code{single-float} or, on
1315 implementations where Infinity and Nan cannot be represented as such,
1316 a special symbol that is treated as if it was Infinity or NaN by the
1317 built-in ordering.
1319 @b{Parameters.} This type is ordered and allows the parameters
1320 @slot{max-inclusive}, @slot{min-inclusive},
1321 @slot{max-exclusive}, and @slot{min-exclusive}."))
1323 (defmethod equal-using-type ((type float-type) u v)
1324 #+(or sbcl allegro) (= u v)
1325 #-(or sbcl allegro) (float= u v))
1327 (defmethod lessp-using-type ((type float-type) u v)
1328 #+(or sbcl allegro) (< u v)
1329 #-(or sbcl allegro) (float< u v))
1331 ;; this one is more complex than would seem necessary, because too-large
1332 ;; and too-small values must be rounded to infinity rather than erroring out
1333 (defun parse-float (e min max +inf -inf nan)
1334 (cond
1335 ((equal e "INF") +inf)
1336 ((equal e "-INF") -inf)
1337 ((equal e "Nan") nan)
1339 (destructuring-bind (&optional a b)
1340 (scan-to-strings "^([^eE]+)(?:[eE]([^eE]+))?$" e)
1341 (if a
1342 (let* ((mantissa (parse/xsd (make-instance 'decimal-type) a nil))
1343 (exponent
1344 (when b
1345 (parse/xsd (make-instance 'integer-type) b nil))))
1346 (if (or (eq mantissa :error) (eq exponent :error))
1347 :error
1348 (let ((ratio (* mantissa (expt 10 (or exponent 1)))))
1349 (cond
1350 ((< ratio min) -inf)
1351 ((> ratio max) +inf)
1352 (t (float ratio min))))))
1353 :error)))))
1355 ;; zzz nehme hier an, dass single-float in IEEE single float ist.
1356 ;; Das stimmt unter LispWorks bestimmt wieder nicht.
1357 (defmethod parse/xsd ((type float-type) e context)
1358 (declare (ignore context))
1359 (parse-float e
1360 most-negative-single-float
1361 most-positive-single-float
1362 single-float-positive-infinity
1363 single-float-negative-infinity
1364 single-float-nan))
1367 ;;; decimal
1369 (defgeneric fraction-digits (data-type)
1370 (:documentation
1371 "@arg[data-type]{a subtype of @class{decimal-type}}
1372 @return{an integer, or @code{nil}}
1373 This slot reader returns the type's
1374 @a[http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits]{fractionDigits facet},
1375 or @code{nil} if none was specified.
1376 @see{total-digits}"))
1378 (defgeneric total-digits (data-type)
1379 (:documentation
1380 "@arg[data-type]{a subtype of @class{decimal-type}}
1381 @return{an integer, or @code{nil}}
1382 This slot reader returns the type's
1383 @a[http://www.w3.org/TR/xmlschema-2/#rf-totalDigits]{totalDigits facet},
1384 or @code{nil} if none was specified.
1385 @see{fraction-digits}"))
1387 (defxsd (decimal-type "decimal") (xsd-type ordering-mixin)
1388 ((fraction-digits :initform nil
1389 :initarg :fraction-digits
1390 :accessor fraction-digits)
1391 (total-digits :initform nil
1392 :initarg :total-digits
1393 :accessor total-digits))
1394 (:documentation
1395 "@short{The decimal data type.}
1397 @b{Syntax.} A rational number, written using an optional decimal point
1398 and decimal places.
1399 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#decimal]{specification}.
1401 @b{Implementation.} This type returns a @code{rational}.
1403 @b{Parameters.} This type is ordered and allows the parameters
1404 @slot{max-inclusive}, @slot{min-inclusive},
1405 @slot{max-exclusive}, and @slot{min-exclusive}.
1407 In addition, the facets @slot{fraction-digits} @slot{total-digits}
1408 are recognized."))
1410 (defmethod describe-facets progn ((object decimal-type) stream)
1411 (dolist (slot '(fraction-digits total-digits))
1412 (let ((value (slot-value object slot)))
1413 (when value
1414 (format stream " ~A ~A"
1415 (intern (symbol-name slot) :keyword)
1416 value)))))
1418 (defmethod parse-parameter
1419 ((class-name (eql 'decimal-type))
1420 (type-name t)
1421 (param (eql :fraction-digits))
1422 value)
1423 (parse (make-instance 'non-negative-integer-type) value nil))
1425 (defmethod parse-parameter
1426 ((class-name (eql 'decimal-type))
1427 (type-name t)
1428 (param (eql :total-digits))
1429 value)
1430 (parse (make-instance 'positive-integer-type) value nil))
1432 (defmethod lessp-using-type ((type decimal-type) u v)
1433 (< u v))
1435 (defmethod equal-using-type ((type decimal-type) u v)
1436 (= u v))
1438 (defmethod validp/xsd and ((type decimal-type) v context)
1439 (declare (ignore context))
1440 (with-slots (fraction-digits total-digits) type
1441 (and (or (null fraction-digits)
1442 (let* ((betrag (abs v))
1443 (fraction (- betrag (truncate betrag)))
1444 (scaled (* fraction (expt 10 fraction-digits))))
1445 (zerop (mod scaled 1))))
1446 (or (null total-digits)
1447 (let ((scaled (abs v)))
1448 (loop
1449 until (zerop (mod scaled 1))
1450 do (setf scaled (* scaled 10)))
1451 (< scaled (expt 10 total-digits)))))))
1453 (defmethod parse/xsd ((type decimal-type) e context)
1454 (declare (ignore context))
1455 (destructuring-bind (&optional a b)
1456 (scan-to-strings "^([+-]?\\d*)(?:[.](\\d+))?$" e)
1457 (if (plusp (+ (length a) (length b)))
1458 (+ (if (plusp (length a))
1459 (parse-integer a)
1461 (if (plusp (length b))
1462 (/ (parse-integer b) (expt 10 (length b)))
1464 :error)))
1467 ;;; double
1469 (defxsd (double-type "double") (xsd-type ordering-mixin)
1471 (:documentation
1472 "@short{The double data type.}
1474 @b{Syntax.} A floating-point number in a \"scientific notation\".
1475 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#double]{specification}.
1477 @b{Implementation.} This type returns a @code{double-float} or, on
1478 implementations where Infinity and Nan cannot be represented as such,
1479 a special symbol that is treated as if it was Infinity or NaN by the
1480 built-in ordering.
1482 @b{Parameters.} This type is ordered and allows the parameters
1483 @slot{max-inclusive}, @slot{min-inclusive},
1484 @slot{max-exclusive}, and @slot{min-exclusive}."))
1486 (defmethod equal-using-type ((type double-type) u v)
1487 #+(or sbcl allegro) (= u v)
1488 #-(or sbcl allegro) (float= u v))
1490 (defmethod lessp-using-type ((type double-type) u v)
1491 #+(or sbcl allegro) (< u v)
1492 #-(or sbcl allegro) (float< u v))
1494 ;; zzz nehme hier an, dass double-float in IEEE double float ist.
1495 ;; Auch das ist nicht garantiert.
1496 (defmethod parse/xsd ((type double-type) e context)
1497 (declare (ignore context))
1498 (parse-float e
1499 most-negative-double-float
1500 most-positive-double-float
1501 double-float-positive-infinity
1502 double-float-negative-infinity
1503 double-float-nan))
1506 ;;; AnyURi
1508 (defxsd (any-uri-type "anyURI") (xsd-type length-mixin)
1510 (:documentation
1511 "@short{The anyURI data type.}
1513 @b{Syntax.} An arbitrary string (!).
1514 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#anyURI]{specification}.
1516 @b{Implementation.} This type returns a normalized string in which
1517 special characters have been escaped.
1519 @b{Parameters.} This type allows restrictions on the length of the
1520 normalized string through the parameters @slot{exact-length},
1521 @slot{min-length}, and @slot{max-length}."))
1523 (defmethod equal-using-type ((type any-uri-type) u v)
1524 (equal u v))
1526 (defmethod parse/xsd ((type any-uri-type) e context)
1527 (cxml-rng::escape-uri e))
1530 ;;; QName
1531 ;;; NOTATION
1533 (defclass qname-like (xsd-type length-mixin) ())
1535 (defxsd (qname-type "QName") (qname-like)
1537 (:documentation
1538 "@short{The QName data type.}
1540 @b{Syntax.} A Qualified Name, as per the \"Namespaces in XML\"
1541 specification. The namespace prefix must be bound to a namespace URI
1542 in the context.
1543 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#QName]{specification}.
1545 @b{Context dependent.} This type is context dependent and requires
1546 the @code{context} argument to @fun{parse} and @fun{validp}.
1548 @b{Implementation.} This type returns a structure with two components,
1549 the namespace URI and the local name. fixme: and the original length.
1550 fixme: export this structure.
1552 @b{Parameters.} This type allows restrictions on the length of the
1553 original QName through the parameters @slot{exact-length},
1554 @slot{min-length}, and @slot{max-length}."))
1556 (defxsd (notation-type "NOTATION") (qname-like)
1558 (:documentation
1559 "@short{The NOTATION data type.}
1561 @b{Syntax.} A qualified name.
1562 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NOTATION]{specification}.
1564 @b{Implementation.} This type is treated exactly like
1565 @class{qname-type}, as specified in
1566 @a[http://relaxng.org/xsd-20010907.html]{Guidelines for using W3C XML
1567 Schema Datatypes with RELAX NG}.
1569 @b{Parameters.} This type allows restrictions on the length of the
1570 original QName through the parameters @slot{exact-length},
1571 @slot{min-length}, and @slot{max-length}."))
1573 (defstruct (qname (:constructor make-qname (uri lname length)))
1575 lname
1576 length)
1578 (defmethod length-using-type ((type qname-like) e)
1579 (qname-length e))
1581 (defmethod equal-using-type ((type qname-like) u v)
1582 (and (equal (qname-uri u) (qname-uri v))
1583 (equal (qname-lname u) (qname-lname v))))
1585 (defun namep (str)
1586 (and (not (zerop (length str)))
1587 (cxml::name-start-rune-p (elt str 0))
1588 (every #'cxml::name-rune-p str)))
1590 (defmethod parse/xsd ((type qname-like) e context)
1591 (handler-case
1592 (if (namep e)
1593 (multiple-value-bind (prefix local-name) (cxml::split-qname e)
1594 (let ((uri (when prefix
1595 (context-find-namespace-binding context prefix))))
1596 (if (and prefix (not uri))
1597 :error
1598 (make-qname uri local-name (length e)))))
1599 :error)
1600 (cxml:well-formedness-violation ()
1601 :error)))
1604 ;;; string
1606 (defxsd (xsd-string-type "string") (xsd-type length-mixin)
1608 (:documentation
1609 "@short{The string data type.}
1611 @b{Syntax.} An arbitrary string.
1612 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#string]{specification}.
1614 @b{Implementation.} Returns the string unchanged. This is the only
1615 XSD type that does not normalize or replace whitespace.
1617 @b{Parameters.} This type allows restrictions on the length of the
1618 string through the parameters @slot{exact-length},
1619 @slot{min-length}, and @slot{max-length}."))
1621 (defmethod equal-using-type ((type xsd-string-type) u v)
1622 (equal u v))
1624 (defmethod munge-whitespace ((type xsd-string-type) e)
1627 (defmethod parse/xsd ((type xsd-string-type) e context)
1631 ;;;;
1632 ;;;; Derived types
1633 ;;;;
1635 ;;; normalizedString
1637 (defxsd (normalized-string-type "normalizedString") (xsd-string-type)
1639 (:documentation
1640 "@short{The normalizedString data type, derived from string.}
1642 @b{Syntax.} An arbitrary string.
1643 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#normalizedString]{specification}.
1645 @b{Implementation.} Returns the string with whitespace replaced.
1647 I.e., each whitespace character is replaced by a space
1648 (character code 32), but multiple spaces, as well as
1649 leading and trailing spaces will still be returned.
1651 (This is the only XSD type that replaces whitespace in this way.)
1653 @b{Parameters.} This type allows restrictions on the length of the
1654 normalized string through the parameters @slot{exact-length},
1655 @slot{min-length}, and @slot{max-length}."))
1657 (defmethod munge-whitespace ((type normalized-string-type) e)
1658 (replace-whitespace e))
1661 ;;; token
1663 (defxsd (xsd-token-type "token") (normalized-string-type)
1665 (:documentation
1666 "@short{The token data type, derived from normalizedString.}
1668 @b{Syntax.} An arbitrary string.
1669 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#token]{specification}.
1671 @b{Implementation.} Returns the string with normalized whitespace.
1673 I.e., each whitespace character is replaced by a space
1674 (character code 32), multiple spaces are collapsed into one character,
1675 and leading and trailing spaces will be removed.
1677 (This is the standard behaviour of all XSD types with the exception of
1678 token's supertypes @class{string-type} and @class{normalized-string-type}.)
1680 @b{Parameters.} This type allows restrictions on the length of the
1681 normalized string through the parameters @slot{exact-length},
1682 @slot{min-length}, and @slot{max-length}."))
1684 (defmethod munge-whitespace ((type xsd-token-type) e)
1685 (normalize-whitespace e))
1688 ;;; language
1690 (defmacro precompile (pattern)
1691 `(load-time-value (list (pattern-scanner ,pattern))))
1693 (defxsd (language-type "language") (xsd-token-type)
1694 ((patterns :initform (precompile "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*")))
1695 (:documentation
1696 "@short{The language data type, derived from token.}
1698 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#language]{specification}.
1700 @b{Restrictions.} This type restricts its supertype @class{token-type}
1701 to strings of the pattern \"[a-zA-Z]{1,8@}(-[a-zA-Z0-9]{1,8@})*\".
1703 @b{Parameters and implementation.} Unchanged from the supertype."))
1706 ;;; Name
1708 (defxsd (name-type "Name") (xsd-token-type)
1709 ((patterns :initform (precompile "\\i\\c*")))
1710 (:documentation
1711 "@short{The Name data type, derived from token.}
1713 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#Name]{specification}.
1715 @b{Restrictions.} This type restricts its supertype @class{token-type}
1716 to strings of the pattern \"\\i\\c*\".
1718 @b{Parameters and implementation.} Unchanged from the supertype."))
1721 ;;; NCName
1723 (defxsd (ncname-type "NCName") (name-type)
1724 ((patterns :initform (precompile "[\\i-[:]][\\c-[:]]*")))
1725 (:documentation
1726 "@short{The NCName data type, derived from Name.}
1728 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NCName]{specification}.
1730 @b{Restrictions.} This type restricts its supertype @class{name-type}
1731 to strings of the pattern \"[\\i-[:]][\\c-[:]]*\".
1733 @b{Parameters and implementation.} Unchanged from the supertype."))
1735 (defmethod equal-using-type ((type ncname-type) u v)
1736 (equal u v))
1738 (defmethod parse/xsd ((type ncname-type) e context)
1742 ;;; ID
1744 (defxsd (id-type "ID") (ncname-type)
1746 (:documentation
1747 "@short{The ID data type, derived from NCName.}
1749 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ID]{specification}.
1751 @b{Restrictions.} None, except when used with DTD compatibility.
1752 See @a[http://relaxng.org/xsd-20010907.html]{Guidelines for using W3C XML
1753 Schema Datatypes with RELAX NG}.
1754 (fixme: not implemented yet -- dfl, 2007-06-06)
1756 @b{Parameters and implementation.} Unchanged from the supertype."))
1759 ;;; IDREF
1761 (defxsd (idref-type "IDREF") (id-type)
1763 (:documentation
1764 "@short{The IDREF data type, derived from ID.}
1766 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#IDREF]{specification}.
1768 @b{Restrictions.} None, except when used with DTD compatibility.
1769 See @a[http://relaxng.org/xsd-20010907.html]{Guidelines for using W3C XML
1770 Schema Datatypes with RELAX NG}.
1771 (fixme: not implemented yet -- dfl, 2007-06-06)
1773 @b{Parameters and implementation.} Unchanged from the supertype."))
1776 ;;; IDREFS
1778 (defxsd (idrefs-type "IDREFS") (enumeration-type)
1779 ((word-type :initform (make-instance 'idref-type)))
1780 (:documentation
1781 "@short{The IDREFS data type, an enumeration.}
1783 @b{Syntax.} A whitespace-separated sequence of @class{idref-type}
1784 values, with at least one element.
1786 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#IDREFS]{specification}.
1788 @b{Implementation.} This type returns a list of the values as returned by
1789 @class{idref-type}.
1791 @b{Parameters.} This type allows restrictions on the number of values
1792 through the parameters @slot{exact-length}, @slot{min-length}, and
1793 @slot{max-length}."))
1796 ;;; ENTITY
1798 (defxsd (entity-type "ENTITY") (ncname-type)
1800 (:documentation
1801 "@short{The ENTITY data type, derived from NCName.}
1803 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ENTITY]{specification}.
1805 @b{Restrictions.} This type restricts its supertype @class{ncname-type}
1806 to names that have been declared as unparsed entities in the context.
1808 @b{Context dependent.} This type is context dependent and requires
1809 the @code{context} argument to @fun{parse} and @fun{validp}.
1811 @b{Parameters and implementation.} Unchanged from the supertype."))
1813 (defmethod parse/xsd ((type entity-type) e context)
1814 (if (context-find-unparsed-entity context e)
1816 :error))
1819 ;;; ENTITIES
1821 (defxsd (entities-type "ENTITIES") (enumeration-type)
1822 ((word-type :initform (make-instance 'entity-type)))
1823 (:documentation
1824 "@short{The ENTITIES data type, an enumeration.}
1826 @b{Syntax.} A whitespace-separated sequence of @class{entity-type}
1827 values, with at least one element.
1829 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#ENTITIES]{specification}.
1831 @b{Implementation.} This type returns a list of the values as returned by
1832 @class{entity-type}.
1834 @b{Context dependent.} This type is context dependent and requires
1835 the @code{context} argument to @fun{parse} and @fun{validp}.
1837 @b{Parameters.} This type allows restrictions on the number of values
1838 through the parameters @slot{exact-length}, @slot{min-length}, and
1839 @slot{max-length}."))
1842 ;;; NMTOKEN
1844 (defxsd (nmtoken-type "NMTOKEN") (xsd-token-type)
1845 ((patterns :initform (precompile "\\c+")))
1846 (:documentation
1847 "@short{The NMTOKEN data type, derived from token.}
1849 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NMTOKEN]{specification}.
1851 @b{Restrictions.} This type restricts its supertype @class{token-type}
1852 to strings of the pattern \"\\c+\".
1854 @b{Parameters and implementation.} Unchanged from the supertype."))
1857 ;;; NMTOKENS
1859 (defxsd (nmtokens-type "NMTOKENS") (enumeration-type)
1860 ((word-type :initform (make-instance 'nmtoken-type)))
1861 (:documentation
1862 "@short{The NMTOKENS data type, an enumeration.}
1864 @b{Syntax.} A whitespace-separated sequence of @class{nmtoken-type}
1865 values, with at least one element.
1867 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#NMTOKENS]{specification}.
1869 @b{Implementation.} This type returns a list of the values as returned by
1870 @class{nmtoken-type}.
1872 @b{Parameters.} This type allows restrictions on the number of values
1873 through the parameters @slot{exact-length}, @slot{min-length}, and
1874 @slot{max-length}."))
1877 ;;; integer
1879 (defxsd (integer-type "integer") (decimal-type)
1881 (:documentation
1882 "@short{The integer data type, derived from decimal.}
1884 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#integer]{specification}.
1886 @b{Syntax.} An integer, written it the decimal system without leading
1887 zeros. No decimal point is permitted.
1889 @b{Implementation.} This type returns an @code{integer}.
1891 @b{Parameters and implementation.} Unchanged from the supertype."))
1893 ;; period is forbidden, so there's no point in letting decimal handle parsing
1894 ;; fixme: sind fuehrende nullen nun erlaubt oder nicht? die spec sagt ja,
1895 ;; das pattern im schema nicht.
1896 (defmethod parse/xsd ((type integer-type) e context)
1897 (declare (ignore context))
1898 (if (cl-ppcre:all-matches "^[+-]?(?:[1-9]\\d*|0)$" e)
1899 (parse-number:parse-number e)
1900 :error))
1903 ;;; nonPositiveInteger
1905 (defxsd (non-positive-integer-type "nonPositiveInteger") (integer-type)
1907 (:documentation
1908 "@short{The nonPositiveInteger data type, derived from integer.}
1910 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#nonPositiveInteger]{specification}.
1912 @b{Restrictions.} This type allows only values <= 0.
1914 @b{Parameters and implementation.} Unchanged from the supertype."))
1916 (defun min* (a b)
1917 (cond
1918 ((null a) b)
1919 ((null b) a)
1920 (t (min a b))))
1922 (defun max* (a b)
1923 (cond
1924 ((null a) b)
1925 ((null b) a)
1926 (t (max a b))))
1928 (defmethod initialize-instance :after ((type non-positive-integer-type) &key)
1929 (setf (max-inclusive type)
1930 (min* 0 (max-inclusive type))))
1933 ;;; nonPositiveInteger
1935 (defxsd (negative-integer-type "negativeInteger") (non-positive-integer-type)
1937 (:documentation
1938 "@short{The negativeInteger data type, derived from nonPositiveInteger.}
1940 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#negativeInteger]{specification}.
1942 @b{Restrictions.} This type allows only values < 0.
1944 @b{Parameters and implementation.} Unchanged from the supertype."))
1946 (defmethod initialize-instance :after ((type negative-integer-type) &key)
1947 (setf (max-inclusive type)
1948 (min* -1 (max-inclusive type))))
1951 ;;; long
1953 (defxsd (long-type "long") (integer-type)
1955 (:documentation
1956 "@short{The long data type, derived from integer.}
1958 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#long]{specification}.
1960 @b{Restrictions.} This type allows only values from the interval
1961 [-2^63, 2^63-1].
1963 @b{Parameters and implementation.} Unchanged from the supertype."))
1965 (defmethod initialize-instance :after ((type long-type) &key)
1966 (setf (max-inclusive type) (min* 9223372036854775807 (max-inclusive type)))
1967 (setf (min-inclusive type) (max* -9223372036854775808 (min-inclusive type))))
1970 ;;; int
1972 (defxsd (int-type "int") (long-type)
1974 (:documentation
1975 "@short{The int data type, derived from long.}
1977 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#int]{specification}.
1979 @b{Restrictions.} This type allows only values from the interval
1980 [-2^31, 2^31-1].
1982 @b{Parameters and implementation.} Unchanged from the supertype."))
1984 (defmethod initialize-instance :after ((type int-type) &key)
1985 (setf (max-inclusive type) (min* 2147483647 (max-inclusive type)))
1986 (setf (min-inclusive type) (max* -2147483648 (min-inclusive type))))
1989 ;;; short
1991 (defxsd (short-type "short") (int-type)
1993 (:documentation
1994 "@short{The short data type, derived from int.}
1996 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#short]{specification}.
1998 @b{Restrictions.} This type allows only values from the interval
1999 [-2^15, 2^15-1].
2001 @b{Parameters and implementation.} Unchanged from the supertype."))
2003 (defmethod initialize-instance :after ((type short-type) &key)
2004 (setf (max-inclusive type) (min* 32767 (max-inclusive type)))
2005 (setf (min-inclusive type) (max* -32768 (min-inclusive type))))
2008 ;;; byte
2010 (defxsd (byte-type "byte") (short-type)
2012 (:documentation
2013 "@short{The byte data type, derived from short.}
2015 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#byte]{specification}.
2017 @b{Restrictions.} This type allows only values from the interval
2018 [-128, 127].
2020 @b{Parameters and implementation.} Unchanged from the supertype."))
2022 (defmethod initialize-instance :after ((type byte-type) &key)
2023 (setf (max-inclusive type) (min* 127 (max-inclusive type)))
2024 (setf (min-inclusive type) (max* -128 (min-inclusive type))))
2027 ;;; nonNegativeInteger
2029 (defxsd (non-negative-integer-type "nonNegativeInteger") (integer-type)
2031 (:documentation
2032 "@short{The nonNegativeInteger data type, derived from integer.}
2034 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#nonNegativeInteger]{specification}.
2036 @b{Restrictions.} This type allows only values >= 0.
2038 @b{Parameters and implementation.} Unchanged from the supertype."))
2040 (defmethod initialize-instance :after ((type non-negative-integer-type) &key)
2041 (setf (min-inclusive type) (max* 0 (min-inclusive type))))
2044 ;;; unsignedLong
2046 (defxsd (unsigned-long-type "unsignedLong") (non-negative-integer-type)
2048 (:documentation
2049 "@short{The unsignedLong data type, derived from nonNegativeInteger.}
2051 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedLong]{specification}.
2053 @b{Restrictions.} This type allows only values from the interval
2054 [0, 2^64-1].
2056 @b{Parameters and implementation.} Unchanged from the supertype."))
2058 (defmethod initialize-instance :after ((type unsigned-long-type) &key)
2059 (setf (max-inclusive type) (min* 18446744073709551615 (max-inclusive type))))
2062 ;;; unsignedInt
2064 (defxsd (unsigned-int-type "unsignedInt") (unsigned-long-type)
2066 (:documentation
2067 "@short{The unsignedInt data type, derived from unsignedLong.}
2069 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedInt]{specification}.
2071 @b{Restrictions.} This type allows only values from the interval
2072 [0, 2^32-1].
2074 @b{Parameters and implementation.} Unchanged from the supertype."))
2076 (defmethod initialize-instance :after ((type unsigned-int-type) &key)
2077 (setf (max-inclusive type) (min* 4294967295 (max-inclusive type))))
2080 ;;; unsignedShort
2082 (defxsd (unsigned-short-type "unsignedShort") (unsigned-int-type)
2084 (:documentation
2085 "@short{The unsignedShort data type, derived from unsignedInt.}
2087 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedShort]{specification}.
2089 @b{Restrictions.} This type allows only values from the interval
2090 [0, 2^16-1].
2092 @b{Parameters and implementation.} Unchanged from the supertype."))
2094 (defmethod initialize-instance :after ((type unsigned-short-type) &key)
2095 (setf (max-inclusive type) (min* 65535 (max-inclusive type))))
2098 ;;; unsignedByte
2100 (defxsd (unsigned-byte-type "unsignedByte") (unsigned-short-type)
2102 (:documentation
2103 "@short{The unsignedByte data type, derived from unsignedInt.}
2105 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#unsignedByte]{specification}.
2107 @b{Restrictions.} This type allows only values from the interval
2108 [0, 255].
2110 @b{Parameters and implementation.} Unchanged from the supertype."))
2112 (defmethod initialize-instance :after ((type unsigned-byte-type) &key)
2113 (setf (max-inclusive type) (min* 255 (max-inclusive type))))
2116 ;;; positiveInteger
2118 (defxsd (positive-integer-type "positiveInteger") (non-negative-integer-type)
2120 (:documentation
2121 "@short{The positiveInteger data type, derived from nonNegativeInteger.}
2123 C.f. the @a[http://www.w3.org/TR/xmlschema-2/#positiveInteger]{specification}.
2125 @b{Restrictions.} This type allows only values > 0.
2127 @b{Parameters and implementation.} Unchanged from the supertype."))
2129 (defmethod initialize-instance :after ((type positive-integer-type) &key)
2130 (setf (min-inclusive type) (max* 1 (min-inclusive type))))