1#
2# Copyright (c), 2016-2020, SISSA (International School for Advanced Studies).
3# All rights reserved.
4# This file is distributed under the terms of the MIT License.
5# See the file 'LICENSE' in the root directory of the present
6# distribution, or http://opensource.org/licenses/MIT.
7#
8# @author Davide Brunato <brunato@sissa.it>
9#
10from decimal import Decimal
11from math import isinf, isnan
12from typing import Optional, Set, Union
13from xml.etree.ElementTree import Element
14from elementpath import datatypes
15
16from ..exceptions import XMLSchemaValueError
17from .exceptions import XMLSchemaValidationError
18
19XSD_FINAL_ATTRIBUTE_VALUES = {'restriction', 'extension', 'list', 'union'}
20
21
22def get_xsd_derivation_attribute(elem: Element, attribute: str,
23                                 values: Optional[Set[str]] = None) -> str:
24    """
25    Get a derivation attribute (maybe 'block', 'blockDefault', 'final' or 'finalDefault')
26    checking the items with the values arguments. Returns a string.
27
28    :param elem: the Element instance.
29    :param attribute: the attribute name.
30    :param values: a set of admitted values when the attribute value is not '#all'.
31    """
32    value = elem.get(attribute)
33    if value is None:
34        return ''
35
36    if values is None:
37        values = XSD_FINAL_ATTRIBUTE_VALUES
38
39    items = value.split()
40    if len(items) == 1 and items[0] == '#all':
41        return ' '.join(values)
42    elif not all(s in values for s in items):
43        raise ValueError("wrong value %r for attribute %r" % (value, attribute))
44    return value
45
46
47#
48# XSD built-in types validator functions
49
50def decimal_validator(value: Union[Decimal, int, float, str]) -> None:
51    try:
52        if not isinstance(value, (Decimal, float)):
53            datatypes.DecimalProxy.validate(value)
54        elif isinf(value) or isnan(value):
55            raise ValueError()
56    except (ValueError, TypeError):
57        raise XMLSchemaValidationError(decimal_validator, value,
58                                       "value is not a valid xs:decimal") from None
59
60
61def qname_validator(value: str) -> None:
62    if datatypes.QName.pattern.match(value) is None:
63        raise XMLSchemaValidationError(qname_validator, value,
64                                       "value is not an xs:QName")
65
66
67def byte_validator(value: int) -> None:
68    if not (-2**7 <= value < 2 ** 7):
69        raise XMLSchemaValidationError(int_validator, value,
70                                       "value must be -128 <= x < 128")
71
72
73def short_validator(value: int) -> None:
74    if not (-2**15 <= value < 2 ** 15):
75        raise XMLSchemaValidationError(short_validator, value,
76                                       "value must be -2^15 <= x < 2^15")
77
78
79def int_validator(value: int) -> None:
80    if not (-2**31 <= value < 2 ** 31):
81        raise XMLSchemaValidationError(int_validator, value,
82                                       "value must be -2^31 <= x < 2^31")
83
84
85def long_validator(value: int) -> None:
86    if not (-2**63 <= value < 2 ** 63):
87        raise XMLSchemaValidationError(long_validator, value,
88                                       "value must be -2^63 <= x < 2^63")
89
90
91def unsigned_byte_validator(value: int) -> None:
92    if not (0 <= value < 2 ** 8):
93        raise XMLSchemaValidationError(unsigned_byte_validator, value,
94                                       "value must be 0 <= x < 256")
95
96
97def unsigned_short_validator(value: int) -> None:
98    if not (0 <= value < 2 ** 16):
99        raise XMLSchemaValidationError(unsigned_short_validator, value,
100                                       "value must be 0 <= x < 2^16")
101
102
103def unsigned_int_validator(value: int) -> None:
104    if not (0 <= value < 2 ** 32):
105        raise XMLSchemaValidationError(unsigned_int_validator, value,
106                                       "value must be 0 <= x < 2^32")
107
108
109def unsigned_long_validator(value: int) -> None:
110    if not (0 <= value < 2 ** 64):
111        raise XMLSchemaValidationError(unsigned_long_validator, value,
112                                       "value must be 0 <= x < 2^64")
113
114
115def negative_int_validator(value: int) -> None:
116    if value >= 0:
117        raise XMLSchemaValidationError(negative_int_validator, value,
118                                       "value must be negative")
119
120
121def positive_int_validator(value: int) -> None:
122    if value <= 0:
123        raise XMLSchemaValidationError(positive_int_validator, value,
124                                       "value must be positive")
125
126
127def non_positive_int_validator(value: int) -> None:
128    if value > 0:
129        raise XMLSchemaValidationError(non_positive_int_validator, value,
130                                       "value must be non positive")
131
132
133def non_negative_int_validator(value: int) -> None:
134    if value < 0:
135        raise XMLSchemaValidationError(non_negative_int_validator, value,
136                                       "value must be non negative")
137
138
139def hex_binary_validator(value: Union[str, datatypes.HexBinary]) -> None:
140    if not isinstance(value, datatypes.HexBinary) and \
141            datatypes.HexBinary.pattern.match(value) is None:
142        raise XMLSchemaValidationError(hex_binary_validator, value,
143                                       "not an hexadecimal number")
144
145
146def base64_binary_validator(value: Union[str, datatypes.Base64Binary]) -> None:
147    if isinstance(value, datatypes.Base64Binary):
148        return
149    value = value.replace(' ', '')
150    if not value:
151        return
152
153    match = datatypes.Base64Binary.pattern.match(value)
154    if match is None or match.group(0) != value:
155        raise XMLSchemaValidationError(base64_binary_validator, value,
156                                       "not a base64 encoding")
157
158
159def error_type_validator(value: object) -> None:
160    raise XMLSchemaValidationError(error_type_validator, value,
161                                   "no value is allowed for xs:error type")
162
163
164#
165# XSD builtin decoding functions
166
167def boolean_to_python(value: str) -> bool:
168    if value in {'true', '1'}:
169        return True
170    elif value in {'false', '0'}:
171        return False
172    else:
173        raise XMLSchemaValueError('{!r} is not a boolean value'.format(value))
174
175
176def python_to_boolean(value: object) -> str:
177    return str(value).lower()
178