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#
10"""
11This module contains classes for XML Schema elements, complex types and model groups.
12"""
13import warnings
14from decimal import Decimal
15from types import GeneratorType
16from typing import TYPE_CHECKING, cast, Any, Dict, Iterator, List, Optional, Tuple, Type, Union
17from elementpath import XPath2Parser, ElementPathError, XPathContext, XPathToken
18from elementpath.datatypes import AbstractDateTime, Duration, AbstractBinary
19
20from ..exceptions import XMLSchemaTypeError, XMLSchemaValueError
21from ..names import XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE, XSD_ALTERNATIVE, \
22    XSD_ELEMENT, XSD_ANY_TYPE, XSD_UNIQUE, XSD_KEY, XSD_KEYREF, XSI_NIL, \
23    XSI_TYPE, XSD_ERROR, XSD_NOTATION_TYPE
24from ..etree import ElementData, etree_element
25from ..aliases import ElementType, SchemaType, BaseXsdType, SchemaElementType, \
26    ModelParticleType, ComponentClassType, AtomicValueType, DecodeType, \
27    IterDecodeType, IterEncodeType
28from ..helpers import get_qname, get_namespace, etree_iter_location_hints, \
29    raw_xml_encode, strictly_equal
30from .. import dataobjects
31from ..converters import XMLSchemaConverter
32from ..xpath import XMLSchemaProtocol, ElementProtocol, XMLSchemaProxy, \
33    ElementPathMixin, XPathElement
34
35from .exceptions import XMLSchemaValidationError, XMLSchemaTypeTableWarning
36from .helpers import get_xsd_derivation_attribute
37from .xsdbase import XSD_TYPE_DERIVATIONS, XSD_ELEMENT_DERIVATIONS, \
38    XsdComponent, ValidationMixin
39from .particles import ParticleMixin, OccursCalculator
40from .identities import IdentityXPathContext, XsdIdentity, XsdKey, XsdUnique, \
41    XsdKeyref, IdentityCounter, IdentityCounterType
42from .simple_types import XsdSimpleType
43from .attributes import XsdAttribute
44from .wildcards import XsdAnyElement
45
46if TYPE_CHECKING:
47    from .attributes import XsdAttributeGroup
48    from .groups import XsdGroup
49
50DataBindingType = Type['dataobjects.DataElement']
51
52
53class XsdElement(XsdComponent, ParticleMixin,
54                 ElementPathMixin[SchemaElementType],
55                 ValidationMixin[ElementType, Any]):
56    """
57    Class for XSD 1.0 *element* declarations.
58
59    :ivar type: the XSD simpleType or complexType of the element.
60    :ivar attributes: the group of the attributes associated with the element.
61
62    ..  <element
63          abstract = boolean : false
64          block = (#all | List of (extension | restriction | substitution))
65          default = string
66          final = (#all | List of (extension | restriction))
67          fixed = string
68          form = (qualified | unqualified)
69          id = ID
70          maxOccurs = (nonNegativeInteger | unbounded)  : 1
71          minOccurs = nonNegativeInteger : 1
72          name = NCName
73          nillable = boolean : false
74          ref = QName
75          substitutionGroup = QName
76          type = QName
77          {any attributes with non-schema namespace . . .}>
78          Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
79        </element>
80    """
81    name: str
82    local_name: str
83    qualified_name: str
84    prefixed_name: str
85
86    parent: Optional['XsdGroup']
87    ref: Optional['XsdElement']
88    attributes: 'XsdAttributeGroup'
89
90    type: BaseXsdType
91    abstract = False
92    nillable = False
93    qualified = False
94    form: Optional[str] = None
95    default: Optional[str] = None
96    fixed: Optional[str] = None
97    substitution_group: Optional[str] = None
98
99    identities: Dict[str, XsdIdentity]
100    alternatives = ()  # type: Union[Tuple[()], List[XsdAlternative]]
101    inheritable = ()  # type: Union[Tuple[()], Dict[str, XsdAttribute]]
102
103    _ADMITTED_TAGS = {XSD_ELEMENT}
104    _block: Optional[str] = None
105    _final: Optional[str] = None
106    _head_type = None
107    _build = True
108
109    binding: Optional[DataBindingType] = None
110
111    def __init__(self, elem: etree_element,
112                 schema: SchemaType,
113                 parent: Optional[XsdComponent] = None,
114                 build: bool = True) -> None:
115
116        if not build:
117            self._build = False
118        super(XsdElement, self).__init__(elem, schema, parent)
119
120    def __repr__(self) -> str:
121        return '%s(%s=%r, occurs=%r)' % (
122            self.__class__.__name__,
123            'name' if self.ref is None else 'ref',
124            self.prefixed_name,
125            list(self.occurs)
126        )
127
128    def __setattr__(self, name: str, value: Any) -> None:
129        if name == "type":
130            if isinstance(value, XsdSimpleType):
131                self.attributes = self.schema.create_empty_attribute_group(self)
132            else:
133                self.attributes = value.attributes
134        super(XsdElement, self).__setattr__(name, value)
135
136    def __iter__(self) -> Iterator[SchemaElementType]:
137        if self.type.has_complex_content():
138            yield from self.type.content.iter_elements()  # type: ignore[union-attr]
139
140    def _parse(self) -> None:
141        if not self._build:
142            return
143
144        self._parse_particle(self.elem)
145        self._parse_attributes()
146
147        if self.ref is None:
148            self._parse_type()
149            self._parse_constraints()
150
151            if self.parent is None and 'substitutionGroup' in self.elem.attrib:
152                self._parse_substitution_group(self.elem.attrib['substitutionGroup'])
153
154    def _parse_attributes(self) -> None:
155        attrib = self.elem.attrib
156        if self._parse_reference():
157            try:
158                xsd_element: XsdElement = self.maps.lookup_element(self.name)
159            except KeyError:
160                self.type = self.any_type
161                self.parse_error('unknown element %r' % self.name)
162            else:
163                self.ref = xsd_element
164                self.type = xsd_element.type
165                self.abstract = xsd_element.abstract
166                self.nillable = xsd_element.nillable
167                self.qualified = xsd_element.qualified
168                self.form = xsd_element.form
169                self.default = xsd_element.default
170                self.fixed = xsd_element.fixed
171                self.substitution_group = xsd_element.substitution_group
172                self.identities = xsd_element.identities
173                self.alternatives = xsd_element.alternatives
174
175            for attr_name in {'type', 'nillable', 'default', 'fixed', 'form',
176                              'block', 'abstract', 'final', 'substitutionGroup'}:
177                if attr_name in attrib:
178                    msg = "attribute {!r} is not allowed when element reference is used"
179                    self.parse_error(msg.format(attr_name))
180            return
181
182        if 'form' in attrib:
183            self.form = attrib['form']
184            if self.form == 'qualified':
185                self.qualified = True
186        elif self.schema.element_form_default == 'qualified':
187            self.qualified = True
188
189        try:
190            if self.parent is None or self.qualified:
191                self.name = get_qname(self.target_namespace, attrib['name'])
192            else:
193                self.name = attrib['name']
194        except KeyError:
195            pass
196
197        if 'abstract' in attrib:
198            if self.parent is not None:
199                self.parse_error("local scope elements cannot have abstract attribute")
200            if attrib['abstract'].strip() in {'true', '1'}:
201                self.abstract = True
202
203        if 'block' in attrib:
204            try:
205                self._block = get_xsd_derivation_attribute(
206                    self.elem, 'block', XSD_ELEMENT_DERIVATIONS
207                )
208            except ValueError as err:
209                self.parse_error(err)
210
211        if 'nillable' in attrib and attrib['nillable'].strip() in {'true', '1'}:
212            self.nillable = True
213
214        if self.parent is None:
215            if 'final' in attrib:
216                try:
217                    self._final = get_xsd_derivation_attribute(
218                        self.elem, 'final', XSD_TYPE_DERIVATIONS
219                    )
220                except ValueError as err:
221                    self.parse_error(err)
222
223            for attr_name in {'ref', 'form', 'minOccurs', 'maxOccurs'}:
224                if attr_name in attrib:
225                    msg = "attribute {!r} is not allowed in a global element declaration"
226                    self.parse_error(msg.format(attr_name))
227        else:
228            for attr_name in {'final', 'substitutionGroup'}:
229                if attr_name in attrib:
230                    msg = "attribute {!r} not allowed in a local element declaration"
231                    self.parse_error(msg.format(attr_name))
232
233    def _parse_type(self) -> None:
234        type_name = self.elem.get('type')
235        if type_name is not None:
236            try:
237                extended_name = self.schema.resolve_qname(type_name)
238            except (KeyError, ValueError, RuntimeError) as err:
239                self.parse_error(err)
240                self.type = self.any_type
241            else:
242                if extended_name == XSD_ANY_TYPE:
243                    self.type = self.any_type
244                else:
245                    try:
246                        self.type = self.maps.lookup_type(extended_name)
247                    except KeyError:
248                        self.parse_error('unknown type {!r}'.format(type_name))
249                        self.type = self.any_type
250            finally:
251                child = self._parse_child_component(self.elem, strict=False)
252                if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE):
253                    self.parse_error("the attribute 'type' and a {} local declaration "
254                                     "are mutually exclusive".format(child.tag.split('}')[-1]))
255        else:
256            child = self._parse_child_component(self.elem, strict=False)
257            if child is None:
258                self.type = self.any_type
259            elif child.tag == XSD_COMPLEX_TYPE:
260                self.type = self.schema.xsd_complex_type_class(child, self.schema, self)
261            elif child.tag == XSD_SIMPLE_TYPE:
262                self.type = self.schema.simple_type_factory(child, self.schema, self)
263            else:
264                self.type = self.any_type
265
266    def _parse_constraints(self) -> None:
267        # Value constraints
268        if 'default' in self.elem.attrib:
269            self.default = self.elem.attrib['default']
270            if 'fixed' in self.elem.attrib:
271                self.parse_error("'default' and 'fixed' attributes are mutually exclusive")
272
273            if not self.type.is_valid(self.default):
274                msg = "'default' value {!r} is not compatible with element's type"
275                self.parse_error(msg.format(self.default))
276                self.default = None
277            elif self.xsd_version == '1.0' and self.type.is_key():
278                self.parse_error("xs:ID or a type derived from xs:ID "
279                                 "cannot have a default value")
280
281        elif 'fixed' in self.elem.attrib:
282            self.fixed = self.elem.attrib['fixed']
283            if not self.type.is_valid(self.fixed):
284                msg = "'fixed' value {!r} is not compatible with element's type"
285                self.parse_error(msg.format(self.fixed))
286                self.fixed = None
287            elif self.xsd_version == '1.0' and self.type.is_key():
288                self.parse_error("xs:ID or a type derived from xs:ID "
289                                 "cannot have a fixed value")
290
291        # Identity constraints
292        self.identities = {}
293        constraint: Union[XsdKey, XsdUnique, XsdKeyref]
294        for child in self.elem:
295            if child.tag == XSD_UNIQUE:
296                constraint = self.schema.xsd_unique_class(child, self.schema, self)
297            elif child.tag == XSD_KEY:
298                constraint = self.schema.xsd_key_class(child, self.schema, self)
299            elif child.tag == XSD_KEYREF:
300                constraint = self.schema.xsd_keyref_class(child, self.schema, self)
301            else:
302                # Invalid tags already caught by validation against the meta-schema
303                continue
304
305            if constraint.ref:
306                if constraint.name in self.identities:
307                    self.parse_error("duplicated identity constraint %r:" % constraint.name, child)
308                self.identities[constraint.name] = constraint
309                continue
310
311            try:
312                if child != self.maps.identities[constraint.name].elem:
313                    self.parse_error("duplicated identity constraint %r:" % constraint.name, child)
314            except KeyError:
315                self.maps.identities[constraint.name] = constraint
316            finally:
317                self.identities[constraint.name] = constraint
318
319    def _parse_substitution_group(self, substitution_group: str) -> None:
320        try:
321            substitution_group_qname = self.schema.resolve_qname(substitution_group)
322        except (KeyError, ValueError, RuntimeError) as err:
323            self.parse_error(err)
324            return
325        else:
326            if substitution_group_qname[0] != '{':
327                substitution_group_qname = get_qname(
328                    self.target_namespace, substitution_group_qname
329                )
330
331        try:
332            head_element = self.maps.lookup_element(substitution_group_qname)
333        except KeyError:
334            self.parse_error("unknown substitutionGroup %r" % substitution_group)
335            return
336        else:
337            if isinstance(head_element, tuple):
338                self.parse_error("circularity found for substitutionGroup %r" % substitution_group)
339                return
340            elif 'substitution' in head_element.block:
341                return
342
343        final = head_element.final
344        if self.type == head_element.type:
345            pass
346        elif self.type.name == XSD_ANY_TYPE:
347            if head_element.type.name != XSD_ANY_TYPE:
348                # Use head element's type for validate content
349                # ref: https://www.w3.org/TR/xmlschema-1/#cElement_Declarations
350                self._head_type = head_element.type
351        elif not self.type.is_derived(head_element.type):
352            self.parse_error("%r type is not of the same or a derivation "
353                             "of the head element %r type." % (self, head_element))
354        elif final == '#all' or 'extension' in final and 'restriction' in final:
355            self.parse_error("head element %r can't be substituted by an element "
356                             "that has a derivation of its type" % head_element)
357        elif 'extension' in final and self.type.is_derived(head_element.type, 'extension'):
358            self.parse_error("head element %r can't be substituted by an element "
359                             "that has an extension of its type" % head_element)
360        elif 'restriction' in final and self.type.is_derived(head_element.type, 'restriction'):
361            self.parse_error("head element %r can't be substituted by an element "
362                             "that has a restriction of its type" % head_element)
363
364        try:
365            self.maps.substitution_groups[substitution_group_qname].add(self)
366        except KeyError:
367            self.maps.substitution_groups[substitution_group_qname] = {self}
368        finally:
369            self.substitution_group = substitution_group_qname
370
371    @property
372    def xpath_proxy(self) -> XMLSchemaProxy:
373        return XMLSchemaProxy(
374            schema=cast(XMLSchemaProtocol, self.schema),
375            base_element=cast(ElementProtocol, self)
376        )
377
378    def build(self) -> None:
379        if self._build:
380            return
381        self._build = True
382        self._parse()
383
384    @property
385    def built(self) -> bool:
386        return hasattr(self, 'type') and \
387            (self.type.parent is None or self.type.built) and \
388            all(c.built for c in self.identities.values())
389
390    @property
391    def validation_attempted(self) -> str:
392        if self.built:
393            return 'full'
394        elif self.type.validation_attempted == 'partial':
395            return 'partial'
396        elif any(c.validation_attempted == 'partial' for c in self.identities.values()):
397            return 'partial'
398        else:
399            return 'none'
400
401    @property
402    def scope(self) -> str:
403        """The scope of the element declaration that can be 'global' or 'local'."""
404        return 'global' if self.parent is None else 'local'
405
406    @property
407    def value_constraint(self) -> Optional[str]:
408        """The fixed or the default value if either is defined, `None` otherwise."""
409        return self.fixed if self.fixed is not None else self.default
410
411    @property
412    def final(self) -> str:
413        if self.ref is not None:
414            return self.ref.final
415        elif self._final is not None:
416            return self._final
417        return self.schema.final_default
418
419    @property
420    def block(self) -> str:
421        if self.ref is not None:
422            return self.ref.block
423        elif self._block is not None:
424            return self._block
425        return self.schema.block_default
426
427    def get_binding(self, *bases: Type[Any], replace_existing: bool = False, **attrs: Any) \
428            -> DataBindingType:
429        """
430        Gets data object binding for XSD element, creating a new one if it doesn't exist.
431
432        :param bases: base classes to use for creating the binding class.
433        :param replace_existing: provide `True` to replace an existing binding class.
434        :param attrs: attribute and method definitions for the binding class body.
435        """
436        if self.binding is None or not replace_existing:
437            if not bases:
438                bases = (dataobjects.DataElement,)
439            attrs['xsd_element'] = self
440            class_name = '{}Binding'.format(self.local_name.title().replace('_', ''))
441            self.binding = cast(DataBindingType,
442                                dataobjects.DataBindingMeta(class_name, bases, attrs))
443        return self.binding
444
445    def get_attribute(self, name: str) -> Optional[XsdAttribute]:
446        if name[0] != '{':
447            name = get_qname(self.type.target_namespace, name)
448        if not isinstance(self.type, XsdSimpleType):
449            xsd_attribute = self.type.attributes[name]
450            assert isinstance(xsd_attribute, XsdAttribute)
451            return xsd_attribute
452        return None
453
454    def get_type(self, elem: Union[ElementType, ElementData],
455                 inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType:
456        return self._head_type or self.type
457
458    def get_attributes(self, xsd_type: BaseXsdType) -> 'XsdAttributeGroup':
459        if not isinstance(xsd_type, XsdSimpleType):
460            return xsd_type.attributes
461        elif xsd_type is self.type:
462            return self.attributes
463        else:
464            return self.schema.create_empty_attribute_group(self)
465
466    def get_path(self, ancestor: Optional[XsdComponent] = None,
467                 reverse: bool = False) -> Optional[str]:
468        """
469        Returns the XPath expression of the element. The path is relative to the schema instance
470        in which the element is contained or is relative to a specific ancestor passed as argument.
471        In the latter case returns `None` if the argument is not an ancestor.
472
473        :param ancestor: optional XSD component of the same schema, that maybe \
474        an ancestor of the element.
475        :param reverse: if set to `True` returns the reverse path, from the element to ancestor.
476        """
477        path: List[str] = []
478        xsd_component: Optional[XsdComponent] = self
479        while xsd_component is not None:
480            if xsd_component is ancestor:
481                return '/'.join(reversed(path)) or '.'
482            elif isinstance(xsd_component, XsdElement):
483                path.append('..' if reverse else xsd_component.name)
484            xsd_component = xsd_component.parent
485        else:
486            if ancestor is None:
487                return '/'.join(reversed(path)) or '.'
488            return None
489
490    def iter_components(self, xsd_classes: Optional[ComponentClassType] = None) \
491            -> Iterator[XsdComponent]:
492
493        if xsd_classes is None:
494            yield self
495            yield from self.identities.values()
496        else:
497            if isinstance(self, xsd_classes):
498                yield self
499            if issubclass(XsdIdentity, xsd_classes):
500                yield from self.identities.values()
501
502        if self.ref is None and self.type.parent is not None:
503            yield from self.type.iter_components(xsd_classes)
504
505    def iter_substitutes(self) -> Iterator['XsdElement']:
506        if self.parent is None or self.ref is not None:
507            for xsd_element in self.maps.substitution_groups.get(self.name, ()):
508                if not xsd_element.abstract:
509                    yield xsd_element
510                for e in xsd_element.iter_substitutes():
511                    if not e.abstract:
512                        yield e
513
514    def data_value(self, elem: ElementType) -> Optional[AtomicValueType]:
515        """Returns the decoded data value of the provided element as XPath fn:data()."""
516        text = elem.text
517        if text is None:
518            text = self.fixed if self.fixed is not None else self.default
519            if text is None:
520                return None
521        return self.type.text_decode(text)
522
523    def check_dynamic_context(self, elem: ElementType, **kwargs: Any) -> None:
524        try:
525            locations = kwargs['locations']
526        except KeyError:
527            return
528
529        schema: Optional[SchemaType]
530        for ns, url in etree_iter_location_hints(elem):
531            if ns not in locations:
532                locations[ns] = url
533            elif locations[ns] is None:
534                reason = "schemaLocation declaration after namespace start"
535                raise XMLSchemaValidationError(self, elem, reason)
536
537            if ns == self.target_namespace:
538                schema = self.schema.include_schema(url, self.schema.base_url)
539            else:
540                schema = self.schema.import_schema(ns, url, self.schema.base_url)
541
542            if schema is None:
543                reason = f"missing dynamic loaded schema from {url}"
544                raise XMLSchemaValidationError(self, elem, reason)
545            elif not schema.built:
546                reason = "dynamic loaded schema change the assessment"
547                raise XMLSchemaValidationError(self, elem, reason)
548
549        if elem.attrib:
550            for name in elem.attrib:
551                if name[0] == '{':
552                    ns = get_namespace(name)
553                    if ns not in locations:
554                        locations[ns] = None
555
556        if elem.tag[0] == '{':
557            ns = get_namespace(elem.tag)
558            if ns not in locations:
559                locations[ns] = None
560
561    def start_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None:
562        """
563        Start tracking of XSD element's identities.
564
565        :param identities: a dictionary containing the identities counters.
566        """
567        for constraint in self.identities.values():
568            try:
569                identities[constraint].clear()
570            except KeyError:
571                identities[constraint] = constraint.get_counter()
572
573    def stop_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None:
574        """
575        Stop tracking of XSD element's identities.
576
577        :param identities: a dictionary containing the identities counters.
578        """
579        for identity in self.identities.values():
580            try:
581                identities[identity].enabled = False
582            except KeyError:
583                identities[identity] = identity.get_counter(enabled=False)
584
585    def iter_decode(self, obj: ElementType, validation: str = 'lax', **kwargs: Any) \
586            -> IterDecodeType[Any]:
587        """
588        Creates an iterator for decoding an Element instance.
589
590        :param obj: the Element that has to be decoded.
591        :param validation: the validation mode, can be 'lax', 'strict' or 'skip'.
592        :param kwargs: keyword arguments for the decoding process.
593        :return: yields a decoded object, eventually preceded by a sequence of \
594        validation or decoding errors.
595        """
596        if self.abstract:
597            reason = "cannot use an abstract element for validation"
598            yield self.validation_error(validation, reason, obj, **kwargs)
599
600        try:
601            namespaces = kwargs['namespaces']
602        except KeyError:
603            namespaces = None
604
605        try:
606            level = kwargs['level']
607        except KeyError:
608            level = kwargs['level'] = 0
609
610        try:
611            identities = kwargs['identities']
612        except KeyError:
613            identities = kwargs['identities'] = {}
614
615        self.start_identities(identities)
616
617        try:
618            converter = kwargs['converter']
619        except KeyError:
620            converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
621        else:
622            if converter is not None and not isinstance(converter, XMLSchemaConverter):
623                converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
624
625        try:
626            pass  # self.check_dynamic_context(elem, **kwargs) TODO: dynamic schema load
627        except XMLSchemaValidationError as err:
628            yield self.validation_error(validation, err, obj, **kwargs)
629
630        inherited = kwargs.get('inherited')
631        value = content = attributes = None
632        nilled = False
633
634        # Get the instance effective type
635        xsd_type = self.get_type(obj, inherited)
636        if XSI_TYPE in obj.attrib:
637            type_name = obj.attrib[XSI_TYPE].strip()
638            try:
639                xsd_type = self.maps.get_instance_type(type_name, xsd_type, namespaces)
640            except (KeyError, TypeError) as err:
641                yield self.validation_error(validation, err, obj, **kwargs)
642            else:
643                if self.identities:
644                    xpath_element = XPathElement(self.name, xsd_type)
645                    for identity in self.identities.values():
646                        if isinstance(identity.elements, tuple):
647                            continue  # Skip unbuilt identities
648
649                        context = IdentityXPathContext(
650                            self.schema, item=xpath_element  # type: ignore[arg-type]
651                        )
652                        for e in identity.selector.token.select_results(context):
653                            if not isinstance(e, XsdElement):
654                                reason = "selector xpath expression can only select elements"
655                                yield self.validation_error(validation, reason, e, **kwargs)
656                            elif e not in identity.elements:
657                                identity.elements[e] = None
658
659            if xsd_type.is_blocked(self):
660                reason = "usage of %r is blocked" % xsd_type
661                yield self.validation_error(validation, reason, obj, **kwargs)
662
663        if xsd_type.abstract:
664            yield self.validation_error(validation, "%r is abstract", obj, **kwargs)
665        if xsd_type.is_complex() and self.xsd_version == '1.1':
666            kwargs['id_list'] = []  # Track XSD 1.1 multiple xs:ID attributes/children
667
668        content_decoder = xsd_type if isinstance(xsd_type, XsdSimpleType) else xsd_type.content
669
670        # Decode attributes
671        attribute_group = self.get_attributes(xsd_type)
672        result: Any
673        for result in attribute_group.iter_decode(obj.attrib, validation, **kwargs):
674            if isinstance(result, XMLSchemaValidationError):
675                yield self.validation_error(validation, result, obj, **kwargs)
676            else:
677                attributes = result
678
679        if self.inheritable and any(name in self.inheritable for name in obj.attrib):
680            if inherited:
681                inherited = inherited.copy()
682                inherited.update((k, v) for k, v in obj.attrib.items() if k in self.inheritable)
683            else:
684                inherited = {k: v for k, v in obj.attrib.items() if k in self.inheritable}
685            kwargs['inherited'] = inherited
686
687        # Checks the xsi:nil attribute of the instance
688        if XSI_NIL in obj.attrib:
689            xsi_nil = obj.attrib[XSI_NIL].strip()
690            if not self.nillable:
691                reason = "element is not nillable."
692                yield self.validation_error(validation, reason, obj, **kwargs)
693            elif xsi_nil not in {'0', '1', 'false', 'true'}:
694                reason = "xsi:nil attribute must have a boolean value."
695                yield self.validation_error(validation, reason, obj, **kwargs)
696            elif xsi_nil in ('0', 'false'):
697                pass
698            elif self.fixed is not None:
699                reason = "xsi:nil='true' but the element has a fixed value."
700                yield self.validation_error(validation, reason, obj, **kwargs)
701            elif obj.text is not None or len(obj):
702                reason = "xsi:nil='true' but the element is not empty."
703                yield self.validation_error(validation, reason, obj, **kwargs)
704            else:
705                nilled = True
706
707        if xsd_type.is_empty() and obj.text and xsd_type.normalize(obj.text):
708            reason = "character data is not allowed because content is empty"
709            yield self.validation_error(validation, reason, obj, **kwargs)
710
711        if nilled:
712            pass
713        elif not isinstance(content_decoder, XsdSimpleType):
714            if not isinstance(xsd_type, XsdSimpleType):
715                for assertion in xsd_type.assertions:
716                    for error in assertion(obj, **kwargs):
717                        yield self.validation_error(validation, error, **kwargs)
718
719            for result in content_decoder.iter_decode(obj, validation, **kwargs):
720                if isinstance(result, XMLSchemaValidationError):
721                    yield self.validation_error(validation, result, obj, **kwargs)
722                else:
723                    content = result
724
725            if content and len(content) == 1 and content[0][0] == 1:
726                value, content = content[0][1], None
727
728            if self.fixed is not None and \
729                    (len(obj) > 0 or value is not None and self.fixed != value):
730                reason = "must have the fixed value %r" % self.fixed
731                yield self.validation_error(validation, reason, obj, **kwargs)
732
733        else:
734            if len(obj):
735                reason = "a simple content element can't have child elements."
736                yield self.validation_error(validation, reason, obj, **kwargs)
737
738            text = obj.text
739            if self.fixed is not None:
740                if text is None:
741                    text = self.fixed
742                elif text == self.fixed:
743                    pass
744                elif not strictly_equal(xsd_type.text_decode(text),
745                                        xsd_type.text_decode(self.fixed)):
746                    reason = "must have the fixed value %r" % self.fixed
747                    yield self.validation_error(validation, reason, obj, **kwargs)
748
749            elif not text and self.default is not None and kwargs.get('use_defaults'):
750                text = self.default
751
752            if not isinstance(xsd_type, XsdSimpleType):
753                for assertion in xsd_type.assertions:
754                    for error in assertion(obj, value=text, **kwargs):
755                        yield self.validation_error(validation, error, **kwargs)
756
757                if text and content_decoder.is_list():
758                    value = text.split()
759                else:
760                    value = text
761
762            elif xsd_type.is_notation():
763                if xsd_type.name == XSD_NOTATION_TYPE:
764                    msg = "cannot validate against xs:NOTATION directly, " \
765                          "only against a subtype with an enumeration facet"
766                    yield self.validation_error(validation, msg, text, **kwargs)
767                elif not xsd_type.enumeration:
768                    msg = "missing enumeration facet in xs:NOTATION subtype"
769                    yield self.validation_error(validation, msg, text, **kwargs)
770
771            if text is None:
772                for result in content_decoder.iter_decode('', validation, **kwargs):
773                    if isinstance(result, XMLSchemaValidationError):
774                        yield self.validation_error(validation, result, obj, **kwargs)
775                        if 'filler' in kwargs:
776                            value = kwargs['filler'](self)
777            else:
778                for result in content_decoder.iter_decode(text, validation, **kwargs):
779                    if isinstance(result, XMLSchemaValidationError):
780                        yield self.validation_error(validation, result, obj, **kwargs)
781                    elif result is None and 'filler' in kwargs:
782                        value = kwargs['filler'](self)
783                    else:
784                        value = result
785
786            if 'value_hook' in kwargs:
787                value = kwargs['value_hook'](value, xsd_type)
788            elif isinstance(value, (int, float, list)) or value is None:
789                pass
790            elif isinstance(value, str):
791                if value.startswith('{') and xsd_type.is_qname():
792                    value = text
793            elif isinstance(value, Decimal):
794                try:
795                    value = kwargs['decimal_type'](value)
796                except (KeyError, TypeError):
797                    pass
798            elif isinstance(value, (AbstractDateTime, Duration)):
799                if not kwargs.get('datetime_types'):
800                    value = str(value) if text is None else text.strip()
801            elif isinstance(value, AbstractBinary):
802                if not kwargs.get('binary_types'):
803                    value = str(value)
804
805        if converter is not None:
806            element_data = ElementData(obj.tag, value, content, attributes)
807            yield converter.element_decode(element_data, self, xsd_type, level)
808        elif not level:
809            yield ElementData(obj.tag, value, None, attributes)
810
811        if content is not None:
812            del content
813
814        # Collects fields values for identities that refer to this element.
815        for identity, counter in identities.items():
816            if not counter.enabled or not identity.elements:
817                continue
818            elif self in identity.elements:
819                xsd_element = self
820            elif self.ref in identity.elements:
821                xsd_element = self.ref
822            else:
823                continue
824
825            try:
826                xsd_fields: Optional[IdentityCounterType]
827                if xsd_type is self.type:
828                    xsd_fields = identity.elements[xsd_element]
829                    if xsd_fields is None:
830                        xsd_fields = identity.get_fields(xsd_element)
831                        identity.elements[xsd_element] = xsd_fields
832                else:
833                    xsd_element = cast(XsdElement, self.copy())
834                    xsd_element.type = xsd_type
835                    xsd_fields = identity.get_fields(xsd_element)
836
837                if all(x is None for x in xsd_fields):
838                    continue
839                decoders = cast(Tuple[XsdAttribute, ...], xsd_fields)
840                fields = identity.get_fields(obj, namespaces, decoders=decoders)
841            except (XMLSchemaValueError, XMLSchemaTypeError) as err:
842                yield self.validation_error(validation, err, obj, **kwargs)
843            else:
844                if any(x is not None for x in fields) or nilled:
845                    try:
846                        counter.increase(fields)
847                    except ValueError as err:
848                        yield self.validation_error(validation, err, obj, **kwargs)
849
850        # Apply non XSD optional validations
851        if 'extra_validator' in kwargs:
852            try:
853                result = kwargs['extra_validator'](obj, self)
854            except XMLSchemaValidationError as err:
855                yield self.validation_error(validation, err, obj, **kwargs)
856            else:
857                if isinstance(result, GeneratorType):
858                    for error in result:
859                        yield self.validation_error(validation, error, obj, **kwargs)
860
861        # Disable collect for out of scope identities and check key references
862        if 'max_depth' not in kwargs:
863            for identity in self.identities.values():
864                counter = identities[identity]
865                counter.enabled = False
866                if isinstance(identity, XsdKeyref):
867                    for error in counter.iter_errors(identities):
868                        yield self.validation_error(validation, error, obj, **kwargs)
869        elif level:
870            self.stop_identities(identities)
871
872    def to_objects(self, obj: ElementType, with_bindings: bool = False, **kwargs: Any) \
873            -> DecodeType['dataobjects.DataElement']:
874        """
875        Decodes XML data to Python data objects.
876
877        :param obj: the XML data source.
878        :param with_bindings: if `True` is provided the decoding is done using \
879        :class:`DataBindingConverter` that used XML data binding classes. For \
880        default the objects are instances of :class:`DataElement` and uses the \
881        :class:`DataElementConverter`.
882        :param kwargs: other optional keyword arguments for the method \
883        :func:`iter_decode`, except the argument *converter*.
884        """
885        if with_bindings:
886            return self.decode(obj, converter=dataobjects.DataBindingConverter, **kwargs)
887        return self.decode(obj, converter=dataobjects.DataElementConverter, **kwargs)
888
889    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
890            -> IterEncodeType[ElementType]:
891        """
892        Creates an iterator for encoding data to an Element.
893
894        :param obj: the data that has to be encoded.
895        :param validation: the validation mode: can be 'lax', 'strict' or 'skip'.
896        :param kwargs: keyword arguments for the encoding process.
897        :return: yields an Element, eventually preceded by a sequence of \
898        validation or encoding errors.
899        """
900        errors: List[Union[str, Exception]] = []
901
902        try:
903            converter = kwargs['converter']
904        except KeyError:
905            converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
906        else:
907            if not isinstance(converter, XMLSchemaConverter):
908                converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
909
910        try:
911            level = kwargs['level']
912        except KeyError:
913            level = 0
914            element_data = converter.element_encode(obj, self, level)
915            if not self.is_matching(element_data.tag, self.default_namespace):
916                errors.append("data tag does not match XSD element name")
917
918            if 'max_depth' in kwargs and kwargs['max_depth'] == 0:
919                for e in errors:
920                    yield self.validation_error(validation, e, **kwargs)
921                return
922        else:
923            element_data = converter.element_encode(obj, self, level)
924
925        text = None
926        children = element_data.content
927        attributes = ()
928
929        xsd_type = self.get_type(element_data)
930        if XSI_TYPE in element_data.attributes:
931            type_name = element_data.attributes[XSI_TYPE].strip()
932            try:
933                xsd_type = self.maps.get_instance_type(type_name, xsd_type, converter)
934            except (KeyError, TypeError) as err:
935                errors.append(err)
936            else:
937                default_namespace = converter.get('')
938                if default_namespace and not isinstance(xsd_type, XsdSimpleType):
939                    # Adjust attributes mapped into default namespace
940
941                    ns_part = '{%s}' % default_namespace
942                    for k in list(element_data.attributes):
943                        if not k.startswith(ns_part):
944                            continue
945                        elif k in xsd_type.attributes:
946                            continue
947
948                        local_name = k[len(ns_part):]
949                        if local_name in xsd_type.attributes:
950                            element_data.attributes[local_name] = element_data.attributes[k]
951                            del element_data.attributes[k]
952
953        attribute_group = self.get_attributes(xsd_type)
954        result: Any
955        for result in attribute_group.iter_encode(element_data.attributes, validation, **kwargs):
956            if isinstance(result, XMLSchemaValidationError):
957                errors.append(result)
958            else:
959                attributes = result
960
961        if XSI_NIL in element_data.attributes:
962            xsi_nil = element_data.attributes[XSI_NIL].strip()
963            if not self.nillable:
964                errors.append("element is not nillable.")
965            elif xsi_nil not in {'0', '1', 'true', 'false'}:
966                errors.append("xsi:nil attribute must has a boolean value.")
967            elif xsi_nil in ('0', 'false'):
968                pass
969            elif self.fixed is not None:
970                errors.append("xsi:nil='true' but the element has a fixed value.")
971            elif element_data.text is not None or element_data.content:
972                errors.append("xsi:nil='true' but the element is not empty.")
973            else:
974                elem = converter.etree_element(element_data.tag, attrib=attributes, level=level)
975                for e in errors:
976                    yield self.validation_error(validation, e, elem, **kwargs)
977                yield elem
978                return
979
980        if isinstance(xsd_type, XsdSimpleType):
981            if element_data.content:
982                errors.append("a simpleType element can't has child elements.")
983
984            if element_data.text is not None:
985                for result in xsd_type.iter_encode(element_data.text, validation, **kwargs):
986                    if isinstance(result, XMLSchemaValidationError):
987                        errors.append(result)
988                    else:
989                        text = result
990
991            elif self.fixed is not None:
992                text = self.fixed
993            elif self.default is not None and kwargs.get('use_defaults'):
994                text = self.default
995
996        elif xsd_type.has_simple_content():
997            if element_data.text is not None:
998                for result in xsd_type.content.iter_encode(element_data.text,
999                                                           validation, **kwargs):
1000                    if isinstance(result, XMLSchemaValidationError):
1001                        errors.append(result)
1002                    else:
1003                        text = result
1004
1005            elif self.fixed is not None:
1006                text = self.fixed
1007            elif self.default is not None and kwargs.get('use_defaults'):
1008                text = self.default
1009
1010        else:
1011            for result in xsd_type.content.iter_encode(element_data, validation, **kwargs):
1012                if isinstance(result, XMLSchemaValidationError):
1013                    errors.append(result)
1014                elif result:
1015                    text, children = result
1016
1017        elem = converter.etree_element(element_data.tag, text, children, attributes, level)
1018
1019        if errors:
1020            for e in errors:
1021                yield self.validation_error(validation, e, elem, **kwargs)
1022        yield elem
1023        del element_data
1024
1025    def is_matching(self, name: Optional[str], default_namespace: Optional[str] = None,
1026                    group: Optional['XsdGroup'] = None, **kwargs: Any) -> bool:
1027        if not name:
1028            return False
1029        elif default_namespace and name[0] != '{':
1030            qname = '{%s}%s' % (default_namespace, name)
1031            if name == self.name or qname == self.name:
1032                return True
1033            return any(name == e.name or qname == e.name for e in self.iter_substitutes())
1034        elif name == self.name:
1035            return True
1036        else:
1037            return any(name == e.name for e in self.iter_substitutes())
1038
1039    def match(self, name: Optional[str], default_namespace: Optional[str] = None,
1040              **kwargs: Any) -> Optional['XsdElement']:
1041        if not name:
1042            return None
1043        elif default_namespace and name[0] != '{':
1044            qname = '{%s}%s' % (default_namespace, name)
1045            if name == self.name or qname == self.name:
1046                return self
1047
1048            for xsd_element in self.iter_substitutes():
1049                if name == xsd_element.name or qname == xsd_element.name:
1050                    return xsd_element
1051
1052        elif name == self.name:
1053            return self
1054        else:
1055            for xsd_element in self.iter_substitutes():
1056                if name == xsd_element.name:
1057                    return xsd_element
1058        return None
1059
1060    def is_restriction(self, other: ModelParticleType, check_occurs: bool = True) -> bool:
1061        e: ModelParticleType
1062
1063        if isinstance(other, XsdAnyElement):
1064            if self.min_occurs == self.max_occurs == 0:
1065                return True
1066            if check_occurs and not self.has_occurs_restriction(other):
1067                return False
1068            return other.is_matching(self.name, self.default_namespace)
1069        elif isinstance(other, XsdElement):
1070            if self.name != other.name:
1071                if other.name == self.substitution_group and \
1072                        other.min_occurs != other.max_occurs and \
1073                        self.max_occurs != 0 and not other.abstract \
1074                        and self.xsd_version == '1.0':
1075                    # An UPA violation case. Base is the head element, it's not
1076                    # abstract and has non deterministic occurs: this is less
1077                    # restrictive than W3C test group (elemZ026), marked as
1078                    # invalid despite it's based on an abstract declaration.
1079                    # See also test case invalid_restrictions1.xsd.
1080                    return False
1081
1082                for e in other.iter_substitutes():
1083                    if e.name == self.name:
1084                        break
1085                else:
1086                    return False
1087
1088            if check_occurs and not self.has_occurs_restriction(other):
1089                return False
1090            elif not self.is_consistent(other) and self.type.elem is not other.type.elem and \
1091                    not self.type.is_derived(other.type, 'restriction') and not other.type.abstract:
1092                return False
1093            elif other.fixed is not None and \
1094                    (self.fixed is None or self.type.normalize(
1095                        self.fixed) != other.type.normalize(other.fixed)):
1096                return False
1097            elif other.nillable is False and self.nillable:
1098                return False
1099            elif any(value not in self.block for value in other.block.split()):
1100                return False
1101            elif not all(k in other.identities for k in self.identities):
1102                return False
1103            else:
1104                return True
1105        elif other.model == 'choice':
1106            if other.is_empty() and self.max_occurs != 0:
1107                return False
1108
1109            check_group_items_occurs = self.xsd_version == '1.0'
1110            total_occurs = OccursCalculator()
1111            for e in other.iter_model():
1112                if not isinstance(e, (XsdElement, XsdAnyElement)):
1113                    return False
1114                elif not self.is_restriction(e, check_group_items_occurs):
1115                    continue
1116                total_occurs += e
1117                total_occurs *= other
1118                if self.has_occurs_restriction(total_occurs):
1119                    return True
1120                total_occurs.reset()
1121            return False
1122        else:
1123            match_restriction = False
1124            for e in other.iter_model():
1125                if match_restriction:
1126                    if not e.is_emptiable():
1127                        return False
1128                elif self.is_restriction(e):
1129                    match_restriction = True
1130                elif not e.is_emptiable():
1131                    return False
1132            return True
1133
1134    def is_overlap(self, other: SchemaElementType) -> bool:
1135        if isinstance(other, XsdElement):
1136            if self.name == other.name:
1137                return True
1138            elif other.substitution_group == self.name or other.name == self.substitution_group:
1139                return True
1140        elif isinstance(other, XsdAnyElement):
1141            if other.is_matching(self.name, self.default_namespace):
1142                return True
1143            for e in self.maps.substitution_groups.get(self.name, ()):
1144                if other.is_matching(e.name, self.default_namespace):
1145                    return True
1146        return False
1147
1148    def is_consistent(self, other: SchemaElementType, strict: bool = True) -> bool:
1149        """
1150        Element Declarations Consistent check between two element particles.
1151
1152        Ref: https://www.w3.org/TR/xmlschema-1/#cos-element-consistent
1153
1154        :returns: `True` if there is no inconsistency between the particles, `False` otherwise,
1155        """
1156        return self.name != other.name or self.type is other.type
1157
1158    def is_single(self) -> bool:
1159        if self.parent is None:
1160            return True
1161        elif self.max_occurs != 1:
1162            return False
1163        elif self.parent.max_occurs == 1:
1164            return True
1165        else:
1166            return self.parent.model != 'choice' and len(self.parent) > 1
1167
1168    def is_empty(self) -> bool:
1169        return self.fixed == '' or self.type.is_empty()
1170
1171
1172class Xsd11Element(XsdElement):
1173    """
1174    Class for XSD 1.1 *element* declarations.
1175
1176    ..  <element
1177          abstract = boolean : false
1178          block = (#all | List of (extension | restriction | substitution))
1179          default = string
1180          final = (#all | List of (extension | restriction))
1181          fixed = string
1182          form = (qualified | unqualified)
1183          id = ID
1184          maxOccurs = (nonNegativeInteger | unbounded)  : 1
1185          minOccurs = nonNegativeInteger : 1
1186          name = NCName
1187          nillable = boolean : false
1188          ref = QName
1189          substitutionGroup = List of QName
1190          targetNamespace = anyURI
1191          type = QName
1192          {any attributes with non-schema namespace . . .}>
1193          Content: (annotation?, ((simpleType | complexType)?, alternative*,
1194          (unique | key | keyref)*))
1195        </element>
1196    """
1197    _target_namespace: Optional[str] = None
1198
1199    def _parse(self) -> None:
1200        if not self._build:
1201            return
1202
1203        self._parse_particle(self.elem)
1204        self._parse_attributes()
1205
1206        if self.ref is None:
1207            self._parse_type()
1208            self._parse_alternatives()
1209            self._parse_constraints()
1210
1211            if self.parent is None and 'substitutionGroup' in self.elem.attrib:
1212                for substitution_group in self.elem.attrib['substitutionGroup'].split():
1213                    self._parse_substitution_group(substitution_group)
1214
1215        self._parse_target_namespace()
1216
1217        if any(v.inheritable for v in self.attributes.values()):
1218            self.inheritable = {}
1219            for k, v in self.attributes.items():
1220                if k is not None and isinstance(v, XsdAttribute):
1221                    if v.inheritable:
1222                        self.inheritable[k] = v
1223
1224    def _parse_alternatives(self) -> None:
1225        alternatives = []
1226        has_test = True
1227        for child in self.elem:
1228            if child.tag == XSD_ALTERNATIVE:
1229                alternatives.append(XsdAlternative(child, self.schema, self))
1230                if not has_test:
1231                    self.parse_error("test attribute missing on non-final alternative")
1232                has_test = 'test' in child.attrib
1233
1234        if alternatives:
1235            self.alternatives = alternatives
1236
1237    @property
1238    def built(self) -> bool:
1239        return (self.type.parent is None or self.type.built) and \
1240            all(c.built for c in self.identities.values()) and \
1241            all(a.built for a in self.alternatives)
1242
1243    @property
1244    def target_namespace(self) -> str:
1245        if self._target_namespace is not None:
1246            return self._target_namespace
1247        elif self.ref is not None:
1248            return self.ref.target_namespace
1249        else:
1250            return self.schema.target_namespace
1251
1252    def iter_components(self, xsd_classes: ComponentClassType = None) -> Iterator[XsdComponent]:
1253        if xsd_classes is None:
1254            yield self
1255            yield from self.identities.values()
1256        else:
1257            if isinstance(self, xsd_classes):
1258                yield self
1259
1260            for obj in self.identities.values():
1261                if isinstance(obj, xsd_classes):
1262                    yield obj
1263
1264        for alt in self.alternatives:
1265            yield from alt.iter_components(xsd_classes)
1266
1267        if self.ref is None and self.type.parent is not None:
1268            yield from self.type.iter_components(xsd_classes)
1269
1270    def iter_substitutes(self) -> Iterator[XsdElement]:
1271        if self.parent is None or self.ref is not None:
1272            for xsd_element in self.maps.substitution_groups.get(self.name, ()):
1273                yield xsd_element
1274                yield from xsd_element.iter_substitutes()
1275
1276    def get_type(self, elem: Union[ElementType, ElementData],
1277                 inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType:
1278        if not self.alternatives:
1279            return self._head_type or self.type
1280
1281        if isinstance(elem, ElementData):
1282            if elem.attributes:
1283                attrib: Dict[str, str] = {}
1284                for k, v in elem.attributes.items():
1285                    value = raw_xml_encode(v)
1286                    if value is not None:
1287                        attrib[k] = value
1288
1289                elem = etree_element(elem.tag, attrib=attrib)
1290            else:
1291                elem = etree_element(elem.tag)
1292
1293        if inherited:
1294            dummy = etree_element('_dummy_element', attrib=inherited)
1295            dummy.attrib.update(elem.attrib)
1296
1297            for alt in self.alternatives:
1298                if alt.type is not None:
1299                    if alt.token is None or alt.test(elem) or alt.test(dummy):
1300                        return alt.type
1301        else:
1302            for alt in self.alternatives:
1303                if alt.type is not None:
1304                    if alt.token is None or alt.test(elem):
1305                        return alt.type
1306
1307        return self._head_type or self.type
1308
1309    def is_overlap(self, other: SchemaElementType) -> bool:
1310        if isinstance(other, XsdElement):
1311            if self.name == other.name:
1312                return True
1313            elif any(self.name == x.name for x in other.iter_substitutes()):
1314                return True
1315
1316            for e in self.iter_substitutes():
1317                if other.name == e.name or any(x is e for x in other.iter_substitutes()):
1318                    return True
1319
1320        elif isinstance(other, XsdAnyElement):
1321            if other.is_matching(self.name, self.default_namespace):
1322                return True
1323            for e in self.maps.substitution_groups.get(self.name, ()):
1324                if other.is_matching(e.name, self.default_namespace):
1325                    return True
1326        return False
1327
1328    def is_consistent(self, other: SchemaElementType, strict: bool = True) -> bool:
1329        if isinstance(other, XsdAnyElement):
1330            if other.process_contents == 'skip':
1331                return True
1332            xsd_element = other.match(self.name, self.default_namespace, resolve=True)
1333            return xsd_element is None or self.is_consistent(xsd_element, strict=False)
1334
1335        e1: XsdElement = self
1336        e2 = other
1337        if self.name != other.name:
1338            for e1 in self.iter_substitutes():
1339                if e1.name == other.name:
1340                    break
1341            else:
1342                for e2 in other.iter_substitutes():
1343                    if e2.name == self.name:
1344                        break
1345                else:
1346                    return True
1347
1348        if len(e1.alternatives) != len(e2.alternatives):
1349            return False
1350        elif e1.type is not e2.type and strict:
1351            return False
1352        elif e1.type is not e2.type or \
1353                not all(any(a == x for x in e2.alternatives) for a in e1.alternatives) or \
1354                not all(any(a == x for x in e1.alternatives) for a in e2.alternatives):
1355            msg = "Maybe a not equivalent type table between elements %r and %r." % (e1, e2)
1356            warnings.warn(msg, XMLSchemaTypeTableWarning, stacklevel=3)
1357        return True
1358
1359
1360class XsdAlternative(XsdComponent):
1361    """
1362    XSD 1.1 type *alternative* definitions.
1363
1364    ..  <alternative
1365          id = ID
1366          test = an XPath expression
1367          type = QName
1368          xpathDefaultNamespace = (anyURI | (##defaultNamespace | ##targetNamespace | ##local))
1369          {any attributes with non-schema namespace . . .}>
1370          Content: (annotation?, (simpleType | complexType)?)
1371        </alternative>
1372    """
1373    parent: XsdElement
1374    type: BaseXsdType
1375    path: Optional[str] = None
1376    token: Optional[XPathToken] = None
1377    _ADMITTED_TAGS = {XSD_ALTERNATIVE}
1378
1379    def __init__(self, elem: ElementType, schema: SchemaType, parent: XsdElement) -> None:
1380        super(XsdAlternative, self).__init__(elem, schema, parent)
1381
1382    def __repr__(self) -> str:
1383        return '%s(type=%r, test=%r)' % (
1384            self.__class__.__name__, self.elem.get('type'), self.elem.get('test')
1385        )
1386
1387    def __eq__(self, other: object) -> bool:
1388        return isinstance(other, XsdAlternative) and \
1389            self.path == other.path and self.type is other.type and \
1390            self.xpath_default_namespace == other.xpath_default_namespace
1391
1392    def __ne__(self, other: object) -> bool:
1393        return not isinstance(other, XsdAlternative) or \
1394            self.path != other.path or self.type is not other.type or \
1395            self.xpath_default_namespace != other.xpath_default_namespace
1396
1397    def _parse(self) -> None:
1398        attrib = self.elem.attrib
1399
1400        if 'xpathDefaultNamespace' in attrib:
1401            self.xpath_default_namespace = self._parse_xpath_default_namespace(self.elem)
1402        else:
1403            self.xpath_default_namespace = self.schema.xpath_default_namespace
1404        parser = XPath2Parser(
1405            namespaces=self.namespaces,
1406            strict=False,
1407            default_namespace=self.xpath_default_namespace
1408        )
1409
1410        try:
1411            self.path = attrib['test']
1412        except KeyError:
1413            pass  # an absent test is not an error, it should be the default type
1414        else:
1415            try:
1416                self.token = parser.parse(self.path)
1417            except ElementPathError as err:
1418                self.parse_error(err)
1419                self.token = parser.parse('false()')
1420                self.path = 'false()'
1421
1422        try:
1423            type_qname = self.schema.resolve_qname(attrib['type'])
1424        except (KeyError, ValueError, RuntimeError) as err:
1425            if 'type' in attrib:
1426                self.parse_error(err)
1427                self.type = self.any_type
1428            else:
1429                child = self._parse_child_component(self.elem, strict=False)
1430                if child is None or child.tag not in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE):
1431                    self.parse_error("missing 'type' attribute")
1432                    self.type = self.any_type
1433                elif child.tag == XSD_COMPLEX_TYPE:
1434                    self.type = self.schema.xsd_complex_type_class(child, self.schema, self)
1435                else:
1436                    self.type = self.schema.simple_type_factory(child, self.schema, self)
1437
1438                if not self.type.is_derived(self.parent.type):
1439                    msg = "declared type is not derived from {!r}"
1440                    self.parse_error(msg.format(self.parent.type))
1441        else:
1442            try:
1443                self.type = self.maps.lookup_type(type_qname)
1444            except KeyError:
1445                self.parse_error("unknown type %r" % attrib['type'])
1446                self.type = self.any_type
1447            else:
1448                if self.type.name != XSD_ERROR and not self.type.is_derived(self.parent.type):
1449                    msg = "type {!r} is not derived from {!r}"
1450                    self.parse_error(msg.format(attrib['type'], self.parent.type))
1451
1452                child = self._parse_child_component(self.elem, strict=False)
1453                if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE):
1454                    self.parse_error("the attribute 'type' and the <%s> local declaration "
1455                                     "are mutually exclusive" % child.tag.split('}')[-1])
1456
1457    @property
1458    def built(self) -> bool:
1459        if not hasattr(self, 'type'):
1460            return False
1461        return self.type.parent is None or self.type.built
1462
1463    @property
1464    def validation_attempted(self) -> str:
1465        if self.built:
1466            return 'full'
1467        elif not hasattr(self, 'type'):
1468            return 'none'
1469        else:
1470            return self.type.validation_attempted
1471
1472    def iter_components(self, xsd_classes: ComponentClassType = None) -> Iterator[XsdComponent]:
1473        if xsd_classes is None or isinstance(self, xsd_classes):
1474            yield self
1475        if self.type is not None and self.type.parent is not None:
1476            yield from self.type.iter_components(xsd_classes)
1477
1478    def test(self, elem: ElementType) -> bool:
1479        if self.token is None:
1480            return False
1481
1482        try:
1483            result = list(self.token.select(context=XPathContext(elem)))
1484            return self.token.boolean_value(result)
1485        except (TypeError, ValueError):
1486            return False
1487