decimal darf mit . starten
[cxml-rng.git] / types.lisp
blobedf24c7f8c155a87706af7b14ca9530d226def98
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]{paramater name, a string}
46 @arg[value]{paramater 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 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}.
94 @see{data-type}"))
96 (defgeneric type-library (type)
97 (:documentation
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.}
102 @see{type-name}
103 @see{type-context-dependent-p}"))
105 (defgeneric type-name (type)
106 (:documentation
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.}
111 @see{type-library}
112 @see{type-context-dependent-p}"))
114 (defmethod find-type ((library t) name params)
115 (declare (ignore name params))
116 nil)
118 (defgeneric type-context-dependent-p (type)
119 (:documentation
120 "@arg[type]{an instance of @class{data-type}}
121 @return{a boolean}
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}
129 @see{type-name}
130 @see{type-library}
131 @see{type-context-dependent-p}"))
133 (defmethod type-context-dependent-p ((type data-type))
134 nil)
136 (defgeneric equal-using-type (type u v)
137 (:documentation
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}}
141 @return{a boolean}
142 @short{Compare the @emph{values} @code{u} and @code{v} using a
143 data-type-dependent equality function.}
145 @see{validp}"))
147 (defgeneric parse (type e &optional context)
148 (:documentation
149 "@arg[type]{an instance of @class{data-type}}
150 @arg[e]{a string}
151 @arg[context]{an instance of @class{validation-context}}
152 @return{an object}
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}
160 @see{validp}"))
162 (defgeneric validp (type e &optional context)
163 (:documentation
164 "@arg[type]{an instance of @class{data-type}}
165 @arg[e]{a string}
166 @arg[context]{an instance of @class{validation-context}}
167 @return{a boolean}
168 @short{Determine whether a string is a valid lexical representation
169 for a type.}
171 The @code{context} argument is required if @fun{type-context-dependent-p}
172 is true for @code{type}, and will be ignored otherwise.
174 @see{parse}
175 @see{equal-using-type}"))
178 ;;; Validation context
180 (defclass validation-context () ()
181 (:documentation
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
188 the Base URI.
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)
198 (:documentation
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
203 current context.}
204 All currently declared namespaces
205 are taken into account, including those declared directly on the
206 current element."))
208 (defgeneric context-find-unparsed-entity (context name)
209 (:documentation
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))
217 (:documentation
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]{
224 klacks source}}
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)))
234 ;; zzz nicht schoen.
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))
241 (return t)))
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))
246 (return t)))))
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))
252 (:documentation
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)
267 (remove prefix
268 (context-stack handler)
269 :count 1
270 :key #'car
271 :test #'equal)))
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) ()
291 (:documentation
292 "@short{The class of Relax NG built-in types.}
293 Relax NG defines two built-in data type: string and token.
295 The Relax NG type library is named @code{:||}."))
297 (defclass string-type (rng-type) ()
298 (:documentation
299 "@short{The Relax NG 'string' type.}
300 This data type allows arbitrary strings and interprets them as-is.
302 For this type, @fun{parse} will return any string unchanged, and
303 @fun{equal-using-type} compares strings using @code{equal}."))
305 (defclass token-type (rng-type) ()
306 (:documentation
307 "@short{The Relax NG 'token' type.}
308 This data type allows arbitrary strings and normalizes all whitespaces.
310 For this type, @fun{parse} will return the string with leading and
311 trailing whitespace removed, and remaining sequences of spaces
312 compressed down to one space character each.
314 A method for @fun{equal-using-type} compares strings using @code{equal}."))
316 (defmethod type-library ((type rng-type))
317 :||)
319 (defvar *string-data-type* (make-instance 'string-type))
320 (defvar *token-data-type* (make-instance 'token-type))
322 (defmethod find-type ((library (eql :||)) name params)
323 (cond
324 ((eq name :probe) t)
325 (params :error)
326 ((equal name "string") *string-data-type*)
327 ((equal name "token") *token-data-type*)
328 (t nil)))
330 (defmethod equal-using-type ((type rng-type) u v)
331 (equal u v))
333 (defmethod validp ((type rng-type) e &optional context)
334 (declare (ignore e context))
337 (defmethod type-name ((type string-type)) "string")
338 (defmethod type-name ((type token-type)) "token")
340 (defmethod parse ((type string-type) e &optional context)
341 (declare (ignore context))
344 (defmethod parse ((type token-type) e &optional context)
345 (declare (ignore context))
346 (normalize-whitespace e))
348 (eval-when (:compile-toplevel :load-toplevel :execute)
349 (defparameter *whitespace*
350 (format nil "~C~C~C~C"
351 (code-char 9)
352 (code-char 32)
353 (code-char 13)
354 (code-char 10))))
356 (defun normalize-whitespace (str)
357 (cl-ppcre:regex-replace-all #.(format nil "[~A]+" *whitespace*)
358 (string-trim *whitespace* str)
359 " "))
361 (defun replace-whitespace (str)
362 (cl-ppcre:regex-replace-all #.(format nil "[~A]" *whitespace*)
364 " "))
367 ;;; XML Schema Part 2: Datatypes Second Edition
369 (defparameter *xsd-types* (make-hash-table :test 'equal))
371 (defmacro defxsd
372 ((class-name type-name) (&rest supers) (&rest slots) &rest args)
373 `(progn
374 (setf (gethash ,type-name *xsd-types*) ',class-name)
375 (defclass ,class-name ,supers
376 ((type-name :initform ,type-name
377 :reader type-name
378 :allocation :class)
379 ,@slots)
380 ,@args)))
382 (defclass xsd-type (data-type)
383 ((patterns :initform nil :initarg :patterns :reader patterns))
384 (:documentation
385 "@short{The class of XML Schema built-in types.}
387 Subclasses of xsd-type provide the built-in types of
388 @a[http://www.w3.org/TR/xmlschema-2/]{
389 XML Schema Part 2: Datatypes Second Edition}
390 as specified in @a[http://relaxng.org/xsd-20010907.html]{Guidelines for
391 using W3C XML Schema Datatypes with RELAX NG}.
393 The XSD type library
394 is named @code{:|http://www.w3.org/2001/XMLSchema-datatypes|}."))
396 (defmethod type-library ((type xsd-type))
397 :|http://www.w3.org/2001/XMLSchema-datatypes|)
399 (defun zip (keys values)
400 (loop for key in keys for value in values collect key collect value))
402 (defgeneric parse-parameter (class-name type-name param-name value))
404 (defun parse-parameters (type-class params)
405 (let ((patterns '())
406 (args '()))
407 (dolist (param params (values t patterns args))
408 (let ((name (param-name param))
409 (value (param-value param)))
410 (if (equal name "pattern")
411 (push value patterns)
412 (multiple-value-bind (key required-class)
413 (case (find-symbol (param-name param) :keyword)
414 (:|length| (values :exact-length 'length-mixin))
415 (:|maxLength| (values :max-length 'length-mixin))
416 (:|minLength| (values :min-length 'length-mixin))
417 (:|minInclusive| (values :min-inclusive 'ordering-mixin))
418 (:|maxInclusive| (values :max-inclusive 'ordering-mixin))
419 (:|minExclusive| (values :min-exclusive 'ordering-mixin))
420 (:|maxExclusive| (values :max-exclusive 'ordering-mixin))
421 (:|totalDigits| (values :total-digits 'decimal-type))
422 (:|fractionDigits| (values :fraction-digits 'decimal-type))
423 (t (return nil)))
424 (unless (subtypep type-class required-class)
425 (return nil))
426 (when (loop
427 for (k nil) on args by #'cddr
428 thereis (eq key k))
429 (return nil))
430 (push (parse-parameter required-class type-class key value) args)
431 (push key args)))))))
433 (defmethod find-type
434 ((library (eql :|http://www.w3.org/2001/XMLSchema-datatypes|)) name params)
435 (if (eq name :probe)
437 (let ((class (gethash name *xsd-types*)))
438 (if class
439 (multiple-value-bind (ok patterns other-args)
440 (parse-parameters class params)
441 (if ok
442 (apply #'make-instance
443 class
444 :patterns patterns
445 other-args)
446 :error))
447 nil))))
449 (defgeneric parse/xsd (type e context))
451 (defgeneric validp/xsd (type v context)
452 (:method-combination and))
454 (defmethod validp/xsd and ((type xsd-type) v context)
455 (declare (ignore context))
456 ;; zzz
457 #+(or)
458 (every (lambda (pattern)
459 (cl-ppcre:all-matches pattern v))
460 (patterns type))
463 (defmethod validp ((type xsd-type) e &optional context)
464 (not (eq :error (parse/xsd type e context))))
466 (defmethod parse ((type xsd-type) e &optional context)
467 (let ((result (parse/xsd type e context)))
468 (when (eq result :error)
469 (error "not valid for data type ~A: ~S" type e))
470 result))
472 ;; Handle the whiteSpace "facet" before the subclass sees it.
473 ;; If parsing succeded, check other facets by asking validp/xsd.
474 (defmethod parse/xsd :around ((type xsd-type) e context)
475 (let ((result (call-next-method type
476 (munge-whitespace type e)
477 context)))
478 (if (or (eq result :error) (validp/xsd type result context))
479 result
480 :error)))
482 (defgeneric munge-whitespace (type e))
484 (defmethod munge-whitespace ((type xsd-type) e)
485 (normalize-whitespace e))
488 ;;; ordering-mixin
490 (defclass ordering-mixin ()
491 ((min-exclusive :initform nil
492 :initarg :min-exclusive
493 :accessor min-exclusive)
494 (max-exclusive :initform nil
495 :initarg :max-exclusive
496 :accessor max-exclusive)
497 (min-inclusive :initform nil
498 :initarg :min-inclusive
499 :accessor min-inclusive)
500 (max-inclusive :initform nil
501 :initarg :max-inclusive
502 :accessor max-inclusive)))
504 (defmethod parse-parameter
505 ((class-name (eql 'ordering-mixin)) type-name (param t) value)
506 (parse (make-instance type-name) value nil))
508 (defgeneric lessp-using-type (type u v)
509 (:documentation
510 "@arg[type]{an ordered @class{data-type}}
511 @arg[u]{a parsed value as returned by @fun{parse}}
512 @arg[v]{a parsed value as returned by @fun{parse}}
513 @return{a boolean}
514 @short{Compare the @emph{values} @code{u} and @code{v} using a
515 data-type-dependent partial ordering.}
517 A method for this function is provided only by types that have a
518 natural partial ordering. The ordering is described in the
519 documentation for the type.
521 @see{equal-using-type}"))
523 (defun <-using-type (type u v)
524 (lessp-using-type type u v))
526 (defun <=-using-type (type u v)
527 (or (lessp-using-type type u v) (equal-using-type type u v)))
529 ;; it's only a partial ordering, so in general this is not the opposite of <=
530 (defun >-using-type (type u v)
531 (lessp-using-type type v u))
533 ;; it's only a partial ordering, so in general this is not the opposite of <
534 (defun >=-using-type (type u v)
535 (or (lessp-using-type type v u) (equal-using-type type v u)))
537 (defmethod validp/xsd and ((type ordering-mixin) v context)
538 (declare (ignore context))
539 (with-slots (min-exclusive max-exclusive min-inclusive max-inclusive) type
540 (and (or (null min-exclusive) (>-using-type type v min-exclusive))
541 (or (null max-exclusive) (<-using-type type v max-exclusive))
542 (or (null min-inclusive) (>=-using-type type v min-inclusive))
543 (or (null max-inclusive) (<=-using-type type v max-inclusive)))))
546 ;;; length-mixin
548 (defclass length-mixin ()
549 ((exact-length :initform nil :initarg :exact-length :accessor exact-length)
550 (min-length :initform nil :initarg :min-length :accessor min-length)
551 (max-length :initform nil :initarg :max-length :accessor max-length)))
553 (defmethod parse-parameter
554 ((class-name (eql 'length-mixin)) (type-name t) (param t) value)
555 (parse (make-instance 'non-negative-integer-type) value nil))
557 ;; extra-hack fuer die "Laenge" eines QName...
558 (defgeneric length-using-type (type u))
559 (defmethod length-using-type ((type length-mixin) e) (length e))
561 (defmethod validp/xsd and ((type length-mixin) v context)
562 (declare (ignore context))
563 (with-slots (exact-length min-length max-length) type
564 (or (not (or exact-length min-length max-length))
565 (let ((l (length-using-type type v)))
566 (and (or (null exact-length) (eql l exact-length))
567 (or (null min-length) (>= l min-length))
568 (or (null max-length) (<= l max-length)))))))
571 ;;; enumeration-type
573 (defclass enumeration-type (xsd-type length-mixin)
574 ((word-type :reader word-type)))
576 (defmethod initialize-instance :after ((type enumeration-type) &key)
577 (setf (min-length type) (max* 1 (min-length type))))
579 (defmethod parse/xsd ((type enumeration-type) e context)
580 (let ((wt (word-type type)))
581 (loop
582 for word in (cl-ppcre:split " " e)
583 for v = (parse wt word context)
584 collect v
585 when (eq v :error) do (return :error))))
589 ;;;; Primitive types
591 ;;; duration
593 (defxsd (duration-type "duration") (xsd-type ordering-mixin) ())
595 (defmethod equal-using-type ((type duration-type) u v)
596 (equal u v))
598 ;; zzz das ist vielleicht ein bisschen zu woertlich implementiert
599 (defmethod lessp-using-type ((type duration-type) u v)
600 (let ((dt (make-instance 'date-time-type)))
601 (every (lambda (str)
602 (let ((s (parse dt str nil)))
603 (lessp-using-type dt
604 (datetime+duration s u)
605 (datetime+duration s v))))
606 '("1696-09-01T00:00:00Z"
607 "1697-02-01T00:00:00Z"
608 "1903-03-01T00:00:00Z"
609 "1903-07-01T00:00:00Z"))))
611 (defun datetime+duration (s d)
612 (destructuring-bind (syear smonth sday shour sminute ssecond szone) s
613 (destructuring-bind (dyear dmonth dday dhour dminute dsecond) d
614 (labels ((floor3 (a low high)
615 (multiple-value-bind (u v)
616 (floor (- a low) (- high low))
617 (values u (+ low v))))
618 (maximum-day-in-month-for (yearvalue monthvalue)
619 (multiple-value-bind (m y)
620 (floor3 monthvalue 1 13)
621 (day-limit m (+ yearvalue y)))))
622 (multiple-value-bind (carry emonth) (floor3 (+ smonth dmonth) 1 13)
623 (let ((eyear (+ syear dyear carry))
624 (ezone szone))
625 (multiple-value-bind (carry esecond) (floor (+ ssecond dsecond) 60)
626 (multiple-value-bind (carry eminute)
627 (floor (+ sminute dminute carry) 60)
628 (multiple-value-bind (carry ehour)
629 (floor (+ shour dhour carry) 24)
630 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
631 (tmpdays (max 1 (min sday mdimf)))
632 (eday (+ tmpdays dday carry)))
633 (loop
634 (let* ((mdimf (maximum-day-in-month-for eyear emonth))
635 (carry
636 (cond
637 ((< eday 1)
638 (setf eday (+ eday mdimf))
640 ((> eday mdimf)
641 (setf eday (- eday mdimf))
644 (return))))
645 (tmp (+ emonth carry)))
646 (multiple-value-bind (y m)
647 (floor3 tmp 1 13)
648 (setf emonth m)
649 (incf eyear y))))
650 (list eyear emonth eday ehour eminute esecond
651 ezone)))))))))))
653 (defun scan-to-strings (&rest args)
654 (coerce (nth-value 1 (apply #'cl-ppcre:scan-to-strings args)) 'list))
656 (defmethod parse/xsd ((type duration-type) e context)
657 (declare (ignore context))
658 (destructuring-bind (&optional minusp y m d tp h min s)
659 (scan-to-strings "(?x)
660 ^(-)? # minus
661 P(?:(\\d+)Y)? # years
662 (?:(\\d+)M)? # months
663 (?:(\\d+)D)? # days
664 (T # (time)
665 (?:(\\d+)H)? # hours
666 (?:(\\d+)M)? # minutes
667 (?:(\\d+(?:[.]\\d+)?)S)? # seconds
668 )?$"
670 (if (and (or y m d h min s)
671 (or (null tp) (or h min s)))
672 (let ((f (if minusp -1 1)))
673 (flet ((int (str)
674 (and str (* f (parse-integer str)))))
675 (list (int y) (int m) (int d) (int h) (int min)
676 (and s (* f (parse-number:parse-number s))))))
677 :error)))
680 ;;; dateTime
682 (defclass time-ordering-mixin (ordering-mixin) ())
684 (defxsd (date-time-type "dateTime") (xsd-type time-ordering-mixin) ())
686 (defmethod equal-using-type ((type time-ordering-mixin) u v)
687 (equal u v))
689 ;; add zone-offset as a duration (if any), but keep a boolean in the
690 ;; zone-offset field indicating whether there was a time-zone
691 (defun normalize-date-time (u)
692 (destructuring-bind (year month day hour minute second zone-offset) u
693 (let ((v (list year month day hour minute second (and zone-offset t))))
694 (if zone-offset
695 (multiple-value-bind (h m)
696 (truncate zone-offset)
697 (datetime+timezone v h (* m 100)))
698 v))))
700 (defun datetime+timezone (d h m)
701 (datetime+duration d (list 0 0 0 h m 0)))
703 (defmethod lessp-using-type ((type time-ordering-mixin) p q)
704 (destructuring-bind (pyear pmonth pday phour pminute psecond pzone)
705 (normalize-date-time p)
706 (destructuring-bind (qyear qmonth qday qhour qminute qsecond qzone)
707 (normalize-date-time q)
708 (cond
709 ((and pzone (not qzone))
710 (lessp-using-type type p (datetime+timezone q 14 0)))
711 ((and (not pzone) qzone)
712 (lessp-using-type type (datetime+timezone p -14 0) q))
714 ;; zzz hier sollen wir <> liefern bei Feldern, die in genau einer
715 ;; der Zeiten fehlen. Wir stellen aber fehlende Felder derzeit
716 ;; defaulted dar, koennen diese Situation also nicht feststellen.
717 ;; Einen Unterschied sollte das nur machen, wenn Werte verschiedener
718 ;; Datentypen miteinander verglichen werden. Das bieten wir einfach
719 ;; nicht an.
720 (loop
721 for a in (list pyear pmonth pday phour pminute psecond)
722 for b in (list qyear qmonth qday qhour qminute qsecond)
724 (when (< a b)
725 (return t))
726 (when (> a b)
727 (return nil))))))))
729 (defun day-limit (m y)
730 (cond
731 ((and (eql m 2)
732 (or (zerop (mod y 400))
733 (and (zerop (mod y 4))
734 (not (zerop (mod y 100))))))
736 ((eql m 2) 28)
737 ((oddp y) 31)
738 (t 30)))
740 (defmethod parse-time (minusp y m d h min s tz tz-sign tz-h tz-m
741 &key (start 0) end)
742 (declare (ignore start end)) ;zzz
743 ;; parse into numbers
744 (flet ((int (str)
745 (and str (parse-integer str)))
746 (num (str)
747 (and str (parse-number:parse-number str))))
748 (setf (values y m d h min s tz-h tz-m)
749 (values (* (int y) (if minusp -1 1))
750 (int m) (int d) (int h) (int min)
751 (num s)
752 (int tz-h) (int tz-m))))
753 (let ((day-limit (day-limit m y)))
754 ;; check ranges
755 (cond
756 ((and y
757 (plusp y)
758 (<= 1 m 12)
759 (<= 1 d day-limit)
760 (<= 0 h 24)
761 (<= 0 m 59)
762 ;; zzz sind leap seconds immer erlaubt?
763 (<= 0 s 60))
764 ;; 24:00:00 must be canonicalized
765 (when (and (eql h 24) (zerop min) (zerop s))
766 (incf h)
767 (incf d)
768 (when (> d day-limit)
769 (setf d 1)
770 (incf m)
771 (when (> m 12)
772 (incf y))))
773 (let ((tz-offset
774 (when tz-h
775 (* (if (equal tz-sign "-") -1 1)
776 (+ tz-h (/ tz-m 100))))))
777 (list (* y (if minusp -1 1)) m d h min s tz-offset)
778 ;; (subseq ... start end)
781 :error))))
783 (defmethod parse/xsd ((type date-time-type) e context)
784 (declare (ignore context))
785 (destructuring-bind (&optional minusp y m d h min s tz tz-sign tz-h tz-m)
786 (scan-to-strings "(?x)
787 ^(-)? # opt. minus
788 ((?:[1-9]\\d*)?\\d{4}) # year
789 -(\\d\\d) # month
790 -(\\d\\d) # day
791 T # (time)
792 (\\d\\d) # hour
793 -(\\d\\d) # minute
794 -(\\d+(?:[.]\\d+)?) # second
795 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
798 (parse-time minusp y m d h min s tz tz-sign tz-h tz-m)))
801 ;;; time
803 (defxsd (time-type "time") (xsd-type time-ordering-mixin) ())
805 (defmethod parse/xsd ((type time-type) e context)
806 (declare (ignore context))
807 (destructuring-bind (&optional h min s tz tz-sign tz-h tz-m)
808 (scan-to-strings "(?x)
809 ^(\\d\\d) # hour
810 -(\\d\\d) # minute
811 -(\\d+(?:[.]\\d+)?) # second
812 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
815 (parse-time nil 1 1 1 h min s tz tz-sign tz-h tz-m
816 :start 3)))
819 ;;; date
821 (defxsd (date-type "date") (xsd-type time-ordering-mixin) ())
823 (defmethod parse/xsd ((type date-type) e context)
824 (declare (ignore context))
825 (destructuring-bind (&optional minusp y m d tz tz-sign tz-h tz-m)
826 (scan-to-strings "(?x)
827 ^(-)? # opt. minus
828 ((?:[1-9]\\d*)?\\d{4}) # year
829 -(\\d\\d) # month
830 -(\\d\\d) # day
831 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
834 (parse-time minusp y m d 0 0 0 tz tz-sign tz-h tz-m
835 :end 3)))
838 ;;; gYearMonth
840 (defxsd (year-month-type "gYearMonth") (xsd-type time-ordering-mixin) ())
842 (defmethod parse/xsd ((type year-month-type) e context)
843 (declare (ignore context))
844 (destructuring-bind (&optional minusp y m)
845 (scan-to-strings "(?x)
846 ^(-)? # opt. minus
847 ((?:[1-9]\\d*)?\\d{4}) # year
848 -(\\d\\d) # month
851 (parse-time minusp y m 1 0 0 0 nil nil nil nil
852 :end 2)))
855 ;;; gYear
857 (defxsd (year-type "gYear") (xsd-type time-ordering-mixin) ())
859 (defmethod parse/xsd ((type year-month-type) e context)
860 (declare (ignore context))
861 (destructuring-bind (&optional minusp y tz tz-sign tz-h tz-m)
862 (scan-to-strings "(?x)
863 ^(-)? # opt. minus
864 ((?:[1-9]\\d*)?\\d{4}) # year
865 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
868 (parse-time minusp y 1 1 0 0 0 tz tz-sign tz-h tz-m
869 :end 1)))
872 ;;; gMonthDay
874 (defxsd (month-day-type "gMonthDay") (xsd-type time-ordering-mixin) ())
876 (defmethod parse/xsd ((type month-day-type) e context)
877 (declare (ignore context))
878 (destructuring-bind (&optional m d tz tz-sign tz-h tz-m)
879 (scan-to-strings "(?x)
880 ^--(\\d\\d) # month
881 -(\\d\\d) # day
882 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
885 (parse-time nil 1 m d 0 0 0 tz tz-sign tz-h tz-m
886 :start 1 :end 3)))
889 ;;; gDay
891 (defxsd (day-type "gDay") (xsd-type time-ordering-mixin) ())
893 (defmethod parse/xsd ((type day-type) e context)
894 (declare (ignore context))
895 (destructuring-bind (&optional d tz tz-sign tz-h tz-m)
896 (scan-to-strings "(?x)
897 ---(\\d\\d) # day
898 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
901 (parse-time nil 1 1 d 0 0 0 tz tz-sign tz-h tz-m
902 :start 3 :end 4)))
905 ;;; gMonth
907 (defxsd (month-type "gMonth") (xsd-type time-ordering-mixin) ())
909 (defmethod parse/xsd ((type month-type) e context)
910 (declare (ignore context))
911 (destructuring-bind (&optional m tz tz-sign tz-h tz-m)
912 (scan-to-strings "(?x)
913 ^--(\\d\\d) # month
914 (([+-])(\\d\\d):(\\d\\d)|Z)? # opt timezone
917 (parse-time nil 1 m 1 0 0 0 tz tz-sign tz-h tz-m
918 :start 2 :end 3)))
921 ;;; boolean
923 (defxsd (boolean-type "boolean") (xsd-type) ())
925 (defmethod parse/xsd ((type boolean-type) e context)
926 (declare (ignore context))
927 (case (find-symbol e :keyword)
928 ((:|true| :|1|) t)
929 ((:|false| :|0|) nil)))
932 ;;; base64Binary
934 (defxsd (base64-binary-type "base64Binary") (xsd-type length-mixin) ())
936 (defmethod equal-using-type ((type base64-binary-type) u v)
937 (equalp u v))
939 (defmethod parse/xsd ((type base64-binary-type) e context)
940 (declare (ignore context))
941 (if (cl-ppcre:all-matches
942 "(?x)
943 ^(([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/]
944 [ ]?[A-Za-z0-9+/][ ]?)*
945 (([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[A-Za-z0-9+/])
946 | ([A-Za-z0-9+/][ ]?[A-Za-z0-9+/][ ]?[AEIMQUYcgkosw048][ ]?=)
947 | ([A-Za-z0-9+/][ ]?[AQgw][ ]?=[ ]?=)))?$"
949 (handler-case
950 (cl-base64:base64-string-to-usb8-array e)
951 (warning (c)
952 (error "unexpected failure in Base64 decoding: ~A" c)))
953 :error))
956 ;;; hexBinary
958 (defxsd (hex-binary-type "hexBinary") (xsd-type length-mixin) ())
960 (defmethod equal-using-type ((type hex-binary-type) u v)
961 (equalp u v))
963 (defmethod parse/xsd ((type hex-binary-type) e context)
964 (declare (ignore context))
965 (if (evenp (length e))
966 (let ((result
967 (make-array (/ (length e) 2) :element-type '(unsigned-byte 8))))
968 (loop
969 for i from 0 below (length e) by 2
970 for j from 0
972 (setf (elt result j)
973 (handler-case
974 (parse-integer e :start i :end (+ i 2) :radix 16)
975 (error ()
976 (return :error))))
977 finally (return result)))
978 :error))
981 ;;; float
983 (defxsd (float-type "float") (xsd-type ordering-mixin) ())
985 (defmethod equal-using-type ((type float-type) u v)
986 (= u v))
988 (defmethod lessp-using-type ((type float-type) u v)
989 (< u v))
991 ;; zzz nehme hier an, dass single-float in IEEE single float ist.
992 ;; Das stimmt unter LispWorks bestimmt wieder nicht.
993 (defmethod parse/xsd ((type float-type) e context)
994 (declare (ignore context))
995 (if (cl-ppcre:all-matches "^[+-]?\\d+([.]\\d+)?([eE][+-]?\\d+)?$" e)
996 (coerce (parse-number:parse-number e) 'single-float)
997 :error))
1000 ;;; decimal
1002 (defxsd (decimal-type "decimal") (xsd-type ordering-mixin)
1003 ((fraction-digits :initform nil
1004 :initarg :fraction-digits
1005 :accessor fraction-digits)
1006 (total-digits :initform nil
1007 :initarg :total-digits
1008 :accessor total-digits)))
1010 (defmethod parse-parameter
1011 ((class-name (eql 'decimal-type))
1012 (type-name t)
1013 (param (eql :fraction-digits))
1014 value)
1015 (parse (make-instance 'non-negative-integer-type) value nil))
1017 (defmethod parse-parameter
1018 ((class-name (eql 'decimal-type))
1019 (type-name t)
1020 (param (eql :total-digits))
1021 value)
1022 (parse (make-instance 'positive-integer-type) value nil))
1024 (defmethod lessp-using-type ((type decimal-type) u v)
1025 (< u v))
1027 (defmethod equal-using-type ((type decimal-type) u v)
1028 (= u v))
1030 (defmethod validp/xsd and ((type decimal-type) v context)
1031 (declare (ignore context))
1032 (with-slots (fraction-digits total-digits) type
1033 (and (or (null fraction-digits)
1034 (let* ((betrag (abs v))
1035 (fraction (- betrag (truncate betrag)))
1036 (scaled (* fraction (expt 10 fraction-digits))))
1037 (zerop (mod scaled 1))))
1038 (or (null total-digits)
1039 (let ((scaled (abs v)))
1040 (loop
1041 until (zerop (mod scaled 1))
1042 do (setf scaled (* scaled 10)))
1043 (< scaled (expt 10 total-digits)))))))
1045 (defmethod parse/xsd ((type decimal-type) e context)
1046 (declare (ignore context))
1047 (destructuring-bind (&optional a b)
1048 (scan-to-strings "^([+-]?\\d*)(?:[.](\\d+))?$" e)
1049 (if (plusp (+ (length a) (length b)))
1050 (+ (if (plusp (length a))
1051 (parse-integer a)
1053 (if (plusp (length b))
1054 (/ (parse-integer b) (expt 10 (length b)))
1056 :error)))
1059 ;;; double
1061 (defxsd (double-type "double") (xsd-type ordering-mixin) ())
1063 (defmethod equal-using-type ((type double-type) u v)
1064 (= u v))
1066 (defmethod lessp-using-type ((type double-type) u v)
1067 (< u v))
1069 ;; zzz nehme hier an, dass double-float in IEEE double float ist.
1070 ;; Auch das ist nicht garantiert.
1071 (defmethod parse/xsd ((type double-type) e context)
1072 (declare (ignore context))
1073 (if (cl-ppcre:all-matches "^[+-]?\\d+([.]\\d+)?([eE][+-]?\\d+)?$" e)
1074 (coerce (parse-number:parse-number e) 'double-float)
1075 :error))
1078 ;;; AnyURi
1080 (defxsd (any-uri-type "anyURI") (xsd-type length-mixin) ())
1082 (defmethod equal-using-type ((type any-uri-type) u v)
1083 (equal u v))
1085 (defmethod parse/xsd ((type any-uri-type) e context)
1086 (cxml-rng::escape-uri e))
1089 ;;; QName
1090 ;;; NOTATION
1092 (defclass qname-like (xsd-type length-mixin) ())
1094 (defxsd (qname-type "QName") (qname-like) ())
1095 (defxsd (notation-type "NOTATION") (qname-like) ())
1097 (defstruct (qname (:constructor make-qname (uri lname length)))
1099 lname
1100 length)
1102 (defmethod length-using-type ((type qname-like) e)
1103 (qname-length e))
1105 (defmethod equal-using-type ((type qname-like) u v)
1106 (and (equal (qname-uri u) (qname-uri v))
1107 (equal (qname-lname u) (qname-lname v))))
1109 (defun namep (str)
1110 (and (not (zerop (length str)))
1111 (cxml::name-start-rune-p (elt str 0))
1112 (every #'cxml::name-rune-p str)))
1114 (defmethod parse/xsd ((type qname-like) e context)
1115 (handler-case
1116 (if (namep e)
1117 (multiple-value-bind (prefix local-name) (cxml::split-qname e)
1118 (let ((uri (when prefix
1119 (context-find-namespace-binding context prefix))))
1120 (if (and prefix (not uri))
1121 :error
1122 (make-qname uri local-name (length e)))))
1123 :error)
1124 (cxml:well-formedness-violation ()
1125 :error)))
1128 ;;; string
1130 (defxsd (xsd-string-type "string") (xsd-type length-mixin) ())
1132 (defmethod equal-using-type ((type xsd-string-type) u v)
1133 (equal u v))
1135 (defmethod munge-whitespace ((type xsd-string-type) e)
1138 (defmethod parse/xsd ((type xsd-string-type) e context)
1142 ;;;;
1143 ;;;; Derived types
1144 ;;;;
1146 ;;; normalizedString
1148 (defxsd (normalized-string-type "normalizedString") (xsd-string-type) ())
1150 (defmethod munge-whitespace ((type normalized-string-type) e)
1151 (replace-whitespace e))
1154 ;;; token
1156 (defxsd (xsd-token-type "token") (normalized-string-type) ())
1158 (defmethod munge-whitespace ((type xsd-token-type) e)
1159 (normalize-whitespace e))
1162 ;;; language
1164 (defxsd (language-type "language") (xsd-token-type)
1165 ((patterns :initform '("[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"))))
1168 ;;; Name
1170 (defxsd (name-type "Name") (xsd-token-type)
1171 ((patterns :initform '("\\i\\c*"))))
1174 ;;; NCName
1176 (defxsd (ncname-type "NCName") (name-type)
1177 ((patterns :initform '("[\\i-[:]][\\c-[:]]*"))))
1179 (defmethod equal-using-type ((type ncname-type) u v)
1180 (equal u v))
1182 (defun nc-name-p (str)
1183 (and (namep str) (cxml::nc-name-p str)))
1185 (defmethod parse/xsd ((type ncname-type) e context)
1186 ;; zzz mit pattern machen
1187 (if (nc-name-p e)
1189 :error))
1191 ;;; ID
1193 (defxsd (id-type "ID") (ncname-type) ())
1196 ;;; IDREF
1198 (defxsd (idref-type "IDREF") (id-type) ())
1201 ;;; IDREFS
1203 (defxsd (idrefs-type "IDREFS") (enumeration-type)
1204 ((word-type :initform (make-instance 'idref-type))))
1207 ;;; ENTITY
1209 (defxsd (entity-type "ENTITY") (ncname-type) ())
1211 (defmethod parse/xsd ((type entity-type) e context)
1212 (if (context-find-unparsed-entity context e)
1214 :error))
1217 ;;; ENTITIES
1219 (defxsd (entities-type "ENTITIES") (enumeration-type)
1220 ((word-type :initform (make-instance 'entity-type))))
1223 ;;; NMTOKEN
1225 (defxsd (nmtoken-type "NMTOKEN") (xsd-token-type)
1226 ((patterns :initform '("\\c+"))))
1229 ;;; NMTOKENS
1231 (defxsd (nmtokens-type "NMTOKENS") (enumeration-type)
1232 ((word-type :initform (make-instance 'nmtoken-type))))
1235 ;;; integer
1237 (defxsd (integer-type "integer") (decimal-type) ())
1239 ;; period is forbidden, so there's no point in letting decimal handle parsing
1240 ;; fixme: sind fuehrende nullen nun erlaubt oder nicht? die spec sagt ja,
1241 ;; das pattern im schema nicht.
1242 (defmethod parse/xsd ((type integer-type) e context)
1243 (declare (ignore context))
1244 (if (cl-ppcre:all-matches "^[+-]?(?:[1-9]\\d*|0)$" e)
1245 (parse-number:parse-number e)
1246 :error))
1249 ;;; nonPositiveInteger
1251 (defxsd (non-positive-integer-type "nonPositiveInteger") (integer-type) ())
1253 (defun min* (a b)
1254 (cond
1255 ((null a) b)
1256 ((null b) a)
1257 (t (min a b))))
1259 (defun max* (a b)
1260 (cond
1261 ((null a) b)
1262 ((null b) a)
1263 (t (max a b))))
1265 (defmethod initialize-instance :after ((type non-positive-integer-type) &key)
1266 (setf (max-inclusive type)
1267 (min* 0 (max-inclusive type))))
1270 ;;; nonPositiveInteger
1272 (defxsd (negative-integer-type "negativeInteger") (non-positive-integer-type)
1275 (defmethod initialize-instance :after ((type negative-integer-type) &key)
1276 (setf (max-inclusive type)
1277 (min* -1 (max-inclusive type))))
1280 ;;; long
1282 (defxsd (long-type "long") (integer-type) ())
1284 (defmethod initialize-instance :after ((type long-type) &key)
1285 (setf (max-inclusive type) (min* 9223372036854775807 (max-inclusive type)))
1286 (setf (min-inclusive type) (max* -9223372036854775808 (min-inclusive type))))
1289 ;;; int
1291 (defxsd (int-type "int") (long-type) ())
1293 (defmethod initialize-instance :after ((type int-type) &key)
1294 (setf (max-inclusive type) (min* 2147483647 (max-inclusive type)))
1295 (setf (min-inclusive type) (max* -2147483648 (min-inclusive type))))
1298 ;;; short
1300 (defxsd (short-type "short") (int-type) ())
1302 (defmethod initialize-instance :after ((type short-type) &key)
1303 (setf (max-inclusive type) (min* 32767 (max-inclusive type)))
1304 (setf (min-inclusive type) (max* -32768 (min-inclusive type))))
1307 ;;; byte
1309 (defxsd (byte-type "byte") (short-type) ())
1311 (defmethod initialize-instance :after ((type byte-type) &key)
1312 (setf (max-inclusive type) (min* 127 (max-inclusive type)))
1313 (setf (min-inclusive type) (max* -128 (min-inclusive type))))
1316 ;;; nonNegativeInteger
1318 (defxsd (non-negative-integer-type "nonNegativeInteger") (integer-type) ())
1320 (defmethod initialize-instance :after ((type non-negative-integer-type) &key)
1321 (setf (min-inclusive type) (max* 0 (min-inclusive type))))
1324 ;;; unsignedLong
1326 (defxsd (unsigned-long-type "unsignedLong") (non-negative-integer-type) ())
1328 (defmethod initialize-instance :after ((type unsigned-long-type) &key)
1329 (setf (max-inclusive type) (min* 18446744073709551615 (max-inclusive type))))
1332 ;;; unsignedInt
1334 (defxsd (unsigned-int-type "unsignedInt") (unsigned-long-type) ())
1336 (defmethod initialize-instance :after ((type unsigned-int-type) &key)
1337 (setf (max-inclusive type) (min* 4294967295 (max-inclusive type))))
1340 ;;; unsignedShort
1342 (defxsd (unsigned-short-type "unsignedShort") (unsigned-int-type) ())
1344 (defmethod initialize-instance :after ((type unsigned-short-type) &key)
1345 (setf (max-inclusive type) (min* 65535 (max-inclusive type))))
1348 ;;; unsignedByte
1350 (defxsd (unsigned-byte-type "unsignedByte") (unsigned-short-type) ())
1352 (defmethod initialize-instance :after ((type unsigned-byte-type) &key)
1353 (setf (max-inclusive type) (min* 255 (max-inclusive type))))
1356 ;;; positiveInteger
1358 (defxsd (positive-integer-type "positiveInteger") (non-negative-integer-type)
1361 (defmethod initialize-instance :after ((type positive-integer-type) &key)
1362 (setf (min-inclusive type) (max* 1 (min-inclusive type))))