class XMLSchemaKeyError(XMLSchemaException, KeyError):
    pass class XMLSchemaTypeError(XMLSchemaException, TypeError):
    pass class XMLSchemaValueError(XMLSchemaException, ValueError):
    pass class XMLSchemaRuntimeError(XMLSchemaException, RuntimeError):
    pass class XMLSchemaWarning(Warning):
    """Base warning class for the XMLSchema package.""" XSD_NAMESPACE = 'http://www.w3.org/2001/XMLSchema' XSD_REDEFINE = _XSD_TEMPLATE % 'redefine' XSD_OVERRIDE = _XSD_TEMPLATE % 'override' XSD_NOTATION = _XSD_TEMPLATE % 'notation' XSD_ANY_TYPE = _XSD_TEMPLATE % 'anyType' XSD_SIMPLE_TYPE = _XSD_TEMPLATE % 'simpleType' XSD_COMPLEX_TYPE = _XSD_TEMPLATE % 'complexType' XSD_GROUP = _XSD_TEMPLATE % 'group' XSD_ATTRIBUTE = _XSD_TEMPLATE % 'attribute' XSD_ATTRIBUTE_GROUP = _XSD_TEMPLATE % 'attributeGroup' XSD_ELEMENT = _XSD_TEMPLATE % 'element' XSI_TYPE = _XSI_TEMPLATE % 'type' C = TypeVar('C')
D = TypeVar('D')
E = TypeVar('E')
T = TypeVar('T') def get_qname(uri: Optional[str], name: str) -> str:
    """
    Returns an expanded QName from URI and local part. If any argument has boolean value
    `False` or if the name is already an expanded QName, returns the *name* argument.

    :param uri: namespace URI
    :param name: local or qualified name
    :return: string or the name argument
    """
    if not uri or not name or name[0] in ('{', '.', '/', '['):
        return name
    else:
        return '{%s}%s' % (uri, name) def local_name(qname: str) -> str:
    """
    Return the local part of an expanded QName or a prefixed name. If the name
    is `None` or empty returns the *name* argument.

    :param qname: an expanded QName or a prefixed name or a local name.
    """
    try:
        if qname[0] == '{':
            _, qname = qname.split('}')
        elif ':' in qname:
            _, qname = qname.split(':')
    except IndexError:
        return ''
    except ValueError:
        raise XMLSchemaValueError("the argument 'qname' has a wrong format: %r" % qname)
    except TypeError:
        raise XMLSchemaTypeError("the argument 'qname' must be a string")
    else:
        return qname def get_extended_qname(qname: str, namespaces: Optional[MutableMapping[str, str]]) -> str:
    """
    Get the extended form of a QName, using a namespace map.
    Local names are mapped to the default namespace.

    :param qname: a prefixed QName or a local name or an extended QName.
    :param namespaces: an optional mapping from prefixes to namespace URIs.
    """
    if not namespaces:
        return qname

    try:
        if qname[0] == '{':
            return qname
    except IndexError:
        return qname

    try:
        prefix, name = qname.split(':', 1)
    except ValueError:
        if not namespaces.get(''):
            return qname
        else:
            return '{%s}%s' % (namespaces[''], qname)
    else:
        try:
            uri = namespaces[prefix]
        except KeyError:
            return qname
        else:
            return '{%s}%s' % (uri, name) if uri else name class NamespaceResourcesMap(MutableMapping[str, Any]):
    """
    Dictionary for storing information about namespace resources. The values are
    lists of objects. Setting an existing value appends the object to the value.
    Setting a value with a list sets/replaces the value.
    """
    __slots__ = ('_store',)

    def __init__(self, *args: Any, **kwargs: Any):
        self._store: Dict[str, List[Any]] = {}
        self.update(*args, **kwargs)

    def __getitem__(self, uri: str) -> Any:
        return self._store[uri]

    def __setitem__(self, uri: str, value: Any) -> None:
        if isinstance(value, list):
            self._store[uri] = value[:]
        else:
            try:
                self._store[uri].append(value)
            except KeyError:
                self._store[uri] = [value]

    def __delitem__(self, uri: str) -> None:
        del self._store[uri]

    def __iter__(self) -> Iterator[str]:
        return iter(self._store)

    def __len__(self) -> int:
        return len(self._store)

    def __repr__(self) -> str:
        return repr(self._store)

    def clear(self) -> None:
        self._store.clear() class XMLSchemaNotBuiltError(XMLSchemaValidatorError, RuntimeError):
    """
    Raised when there is an improper usage attempt of a not built XSD validator.

    :param validator: the XSD validator.
    :param message: the error message.
    """
    def __init__(self, validator: 'XsdValidator', message: str) -> None:
        super(XMLSchemaNotBuiltError, self).__init__(
            validator=validator,
            message=message,
            elem=getattr(validator, 'elem', None),
            source=getattr(validator, 'source', None),
            namespaces=getattr(validator, 'namespaces', None)
        ) class XMLSchemaModelError(XMLSchemaValidatorError, ValueError):
    """
    Raised when a model error is found during the checking of a model group.

    :param group: the XSD model group.
    :param message: the error message.
    """
    def __init__(self, group: 'XsdGroup', message: str) -> None:
        super(XMLSchemaModelError, self).__init__(
            validator=group,
            message=message,
            elem=getattr(group, 'elem', None),
            source=getattr(group, 'source', None),
            namespaces=getattr(group, 'namespaces', None)
        ) class XMLSchemaModelDepthError(XMLSchemaModelError):
    """Raised when recursion depth is exceeded while iterating a model group."""
    def __init__(self, group: 'XsdGroup') -> None:
        msg = "maximum model recursion depth exceeded while iterating {!r}".format(group)
        super(XMLSchemaModelDepthError, self).__init__(group, message=msg) class XMLSchemaParseError(XMLSchemaValidatorError, SyntaxError):  # type: ignore[misc]
    """
    Raised when an error is found during the building of an XSD validator.

    :param validator: the XSD validator.
    :param message: the error message.
    :param elem: the element that contains the error.
    """
    def __init__(self, validator: 'XsdValidator', message: str,
                 elem: Optional[ElementType] = None) -> None:
        super(XMLSchemaParseError, self).__init__(
            validator=validator,
            message=message,
            elem=elem if elem is not None else getattr(validator, 'elem', None),
            source=getattr(validator, 'source', None),
            namespaces=getattr(validator, 'namespaces', None),
        ) class XsdValidator:
    """
    Common base class for XML Schema validator, that represents a PSVI (Post Schema Validation
    Infoset) information item. A concrete XSD validator have to report its validity collecting
    building errors and implementing the properties.

    :param validation: defines the XSD validation mode to use for build the validator, \
    its value can be 'strict', 'lax' or 'skip'. Strict mode is the default.
    :type validation: str

    :ivar validation: XSD validation mode.
    :vartype validation: str
    :ivar errors: XSD validator building errors.
    :vartype errors: list
    """
    elem: Optional[etree_element] = None
    namespaces: Any = None
    errors: List[XMLSchemaParseError]

    def __init__(self, validation: str = 'strict') -> None:
        self.validation = validation
        self.errors = []

    @property
    def built(self) -> bool:
        """
        Property that is ``True`` if XSD validator has been fully parsed and built,
        ``False`` otherwise. For schemas the property is checked on all global
        components. For XSD components check only the building of local subcomponents.
        """
        raise NotImplementedError()

    @property
    def validation_attempted(self) -> str:
        """
        Property that returns the *validation status* of the XSD validator.
        It can be 'full', 'partial' or 'none'.

        | https://www.w3.org/TR/xmlschema-1/#e-validation_attempted
        | https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#e-validation_attempted
        """
        raise NotImplementedError()

    @property
    def validity(self) -> str:
        """
        Property that returns the XSD validator's validity.
        It can be ‘valid’, ‘invalid’ or ‘notKnown’.

        | https://www.w3.org/TR/xmlschema-1/#e-validity
        | https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#e-validity
        """
        if self.validation == 'skip':
            return 'notKnown'
        elif self.errors or any(comp.errors for comp in self.iter_components()):
            return 'invalid'
        elif self.built:
            return 'valid'
        else:
            return 'notKnown'

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[Union['XsdComponent', SchemaType, 'XsdGlobals']]:
        """
        Creates an iterator for traversing all XSD components of the validator.

        :param xsd_classes: returns only a specific class/classes of components, \
        otherwise returns all components.
        """
        raise NotImplementedError()

    @property
    def all_errors(self) -> List[XMLSchemaParseError]:
        """
        A list with all the building errors of the XSD validator and its components.
        """
        errors = []
        for comp in self.iter_components():
            if comp.errors:
                errors.extend(comp.errors)
        return errors

    def copy(self) -> 'XsdValidator':
        validator: 'XsdValidator' = object.__new__(self.__class__)
        validator.__dict__.update(self.__dict__)
        validator.errors = self.errors[:]  # shallow copy duplicates errors list
        return validator

    __copy__ = copy

    def parse_error(self, error: Union[str, Exception],
                    elem: Optional[ElementType] = None,
                    validation: Optional[str] = None) -> None:
        """
        Helper method for registering parse errors. Does nothing if validation mode is 'skip'.
        Il validation mode is 'lax' collects the error, otherwise raise the error.

        :param error: can be a parse error or an error message.
        :param elem: the Element instance related to the error, for default uses the 'elem' \
        attribute of the validator, if it's present.
        :param validation: overrides the default validation mode of the validator.
        """
        if validation is not None:
            check_validation_mode(validation)
        else:
            validation = self.validation

        if validation == 'skip':
            return
        elif elem is None:
            elem = self.elem
        elif not is_etree_element(elem):
            msg = "the argument 'elem' must be an Element instance, not {!r}."
            raise XMLSchemaTypeError(msg.format(elem))

        if isinstance(error, XMLSchemaParseError):
            error.validator = self
            error.namespaces = getattr(self, 'namespaces', None)
            error.elem = elem
            error.source = getattr(self, 'source', None)
        elif isinstance(error, Exception):
            message = str(error).strip()
            if message[0] in '\'"' and message[0] == message[-1]:
                message = message.strip('\'"')
            error = XMLSchemaParseError(self, message, elem)
        elif isinstance(error, str):
            error = XMLSchemaParseError(self, error, elem)
        else:
            msg = "'error' argument must be an exception or a string, not {!r}."
            raise XMLSchemaTypeError(msg.format(error))

        if validation == 'lax':
            self.errors.append(error)
        else:
            raise error

    def validation_error(self, validation: str,
                         error: Union[str, Exception],
                         obj: Any = None,
                         source: Optional[XMLResource] = None,
                         namespaces: Optional[NamespacesType] = None,
                         **_kwargs: Any) -> XMLSchemaValidationError:
        """
        Helper method for generating and updating validation errors. If validation
        mode is 'lax' or 'skip' returns the error, otherwise raises the error.

        :param validation: an error-compatible validation mode: can be 'lax' or 'strict'.
        :param error: an error instance or the detailed reason of failed validation.
        :param obj: the instance related to the error.
        :param source: the XML resource related to the validation process.
        :param namespaces: is an optional mapping from namespace prefix to URI.
        :param _kwargs: keyword arguments of the validation process that are not used.
        """
        check_validation_mode(validation)
        if isinstance(error, XMLSchemaValidationError):
            if error.namespaces is None and namespaces is not None:
                error.namespaces = namespaces
            if error.source is None and source is not None:
                error.source = source
            if error.obj is None and obj is not None:
                error.obj = obj
            if error.elem is None and obj is not None and is_etree_element(obj):
                error.elem = obj
                if is_etree_element(error.obj) and obj.tag == error.obj.tag:
                    error.obj = obj

        elif isinstance(error, Exception):
            error = XMLSchemaValidationError(self, obj, str(error), source, namespaces)
        else:
            error = XMLSchemaValidationError(self, obj, error, source, namespaces)

        if validation == 'strict' and error.elem is not None:
            raise error
        return error

    def _parse_xpath_default_namespace(self, elem: ElementType) -> str:
        """
        Parse XSD 1.1 xpathDefaultNamespace attribute for schema, alternative, assert, assertion
        and selector declarations, checking if the value is conforming to the specification. In
        case the attribute is missing or for wrong attribute values defaults to ''.
        """
        try:
            value = elem.attrib['xpathDefaultNamespace']
        except KeyError:
            return ''

        value = value.strip()
        if value == '##local':
            return ''
        elif value == '##defaultNamespace':
            default_namespace = getattr(self, 'default_namespace', None)
            return default_namespace if isinstance(default_namespace, str) else ''
        elif value == '##targetNamespace':
            target_namespace = getattr(self, 'target_namespace', '')
            return target_namespace if isinstance(target_namespace, str) else ''
        elif len(value.split()) == 1:
            return value
        else:
            admitted_values = ('##defaultNamespace', '##targetNamespace', '##local')
            msg = "wrong value %r for 'xpathDefaultNamespace' attribute, can be (anyURI | %s)."
            self.parse_error(msg % (value, ' | '.join(admitted_values)), elem)
            return '' class XsdComponent(XsdValidator):
    """
    Class for XSD components. See: https://www.w3.org/TR/xmlschema-ref/

    :param elem: ElementTree's node containing the definition.
    :param schema: the XMLSchema object that owns the definition.
    :param parent: the XSD parent, `None` means that is a global component that \
    has the schema as parent.
    :param name: name of the component, maybe overwritten by the parse of the `elem` argument.

    :cvar qualified: for name matching, unqualified matching may be admitted only \
    for elements and attributes.
    :vartype qualified: bool
    """
    _REGEX_SPACE = re.compile(r'\s')
    _REGEX_SPACES = re.compile(r'\s+')
    _ADMITTED_TAGS: Union[Set[str], Tuple[str, ...], Tuple[()]] = ()

    elem: etree_element
    parent = None
    name = None
    ref: Optional['XsdComponent'] = None
    qualified = True
    redefine = None
    _annotation = None
    _target_namespace: Optional[str]

    def __init__(self, elem: etree_element,
                 schema: SchemaType,
                 parent: Optional['XsdComponent'] = None,
                 name: Optional[str] = None) -> None:

        super(XsdComponent, self).__init__(schema.validation)
        if name:
            self.name = name
        if parent is not None:
            self.parent = parent
        self.schema = schema
        self.maps: XsdGlobals = schema.maps
        self.elem = elem

    def __setattr__(self, name: str, value: Any) -> None:
        if name == 'elem':
            if value.tag not in self._ADMITTED_TAGS:
                msg = "wrong XSD element {!r} for {!r}, must be one of {!r}"
                raise XMLSchemaValueError(
                    msg.format(value.tag, self.__class__, self._ADMITTED_TAGS)
                )
            super(XsdComponent, self).__setattr__(name, value)
            if self.errors:
                self.errors.clear()
            self._parse()
        else:
            super(XsdComponent, self).__setattr__(name, value)

    @property
    def xsd_version(self) -> str:
        return self.schema.XSD_VERSION

    def is_global(self) -> bool:
        """Returns `True` if the instance is a global component, `False` if it's local."""
        return self.parent is None

    def is_override(self) -> bool:
        """Returns `True` if the instance is an override of a global component."""
        if self.parent is not None:
            return False
        return any(self.elem in x for x in self.schema.root if x.tag == XSD_OVERRIDE)

    @property
    def schema_elem(self) -> ElementType:
        """The reference element of the schema for the component instance."""
        return self.elem

    @property
    def source(self) -> XMLResource:
        """Property that references to schema source."""
        return self.schema.source

    @property
    def target_namespace(self) -> str:
        """Property that references to schema's targetNamespace."""
        return self.schema.target_namespace if self.ref is None else self.ref.target_namespace

    @property
    def default_namespace(self) -> Optional[str]:
        """Property that references to schema's default namespaces."""
        return self.schema.namespaces.get('')

    @property
    def namespaces(self) -> NamespacesType:
        """Property that references to schema's namespace mapping."""
        return self.schema.namespaces

    @property
    def any_type(self) -> 'XsdComplexType':
        """Property that references to the xs:anyType instance of the global maps."""
        return cast('XsdComplexType', self.maps.types[XSD_ANY_TYPE])

    @property
    def any_simple_type(self) -> 'XsdSimpleType':
        """Property that references to the xs:anySimpleType instance of the global maps."""
        return cast('XsdSimpleType', self.maps.types[XSD_ANY_SIMPLE_TYPE])

    @property
    def any_atomic_type(self) -> 'XsdSimpleType':
        """Property that references to the xs:anyAtomicType instance of the global maps."""
        return cast('XsdSimpleType', self.maps.types[XSD_ANY_ATOMIC_TYPE])

    @property
    def annotation(self) -> Optional['XsdAnnotation']:
        if '_annotation' not in self.__dict__:
            for child in self.elem:
                if child.tag == XSD_ANNOTATION:
                    self._annotation = XsdAnnotation(child, self.schema, self)
                    break
                elif not callable(child.tag):
                    self._annotation = None
                    break
            else:
                self._annotation = None

        return self._annotation

    def __repr__(self) -> str:
        if self.ref is not None:
            return '%s(ref=%r)' % (self.__class__.__name__, self.prefixed_name)
        else:
            return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name)

    def _parse(self) -> None:
        return

    def _parse_reference(self) -> Optional[bool]:
        """
        Helper method for referable components. Returns `True` if a valid reference QName
        is found without any error, otherwise returns `None`. Sets an id-related name for
        the component ('nameless_<id of the instance>') if both the attributes 'ref' and
        'name' are missing.
        """
        ref = self.elem.get('ref')
        if ref is None:
            if 'name' in self.elem.attrib:
                return None
            elif self.parent is None:
                self.parse_error("missing attribute 'name' in a global %r" % type(self))
            else:
                self.parse_error(
                    "missing both attributes 'name' and 'ref' in local %r" % type(self)
                )
        elif 'name' in self.elem.attrib:
            self.parse_error("attributes 'name' and 'ref' are mutually exclusive")
        elif self.parent is None:
            self.parse_error("attribute 'ref' not allowed in a global %r" % type(self))
        else:
            try:
                self.name = self.schema.resolve_qname(ref)
            except (KeyError, ValueError, RuntimeError) as err:
                self.parse_error(err)
            else:
                if self._parse_child_component(self.elem, strict=False) is not None:
                    self.parse_error("a reference component cannot have "
                                     "child definitions/declarations")
                return True

        return None

    def _parse_child_component(self, elem: ElementType, strict: bool = True) \
            -> Optional[ElementType]:
        child = None
        for e in elem:
            if e.tag == XSD_ANNOTATION or callable(e.tag):
                continue
            elif not strict:
                return e
            elif child is not None:
                msg = "too many XSD components, unexpected {!r} found at position {}"
                self.parse_error(msg.format(child, elem[:].index(e)), elem)
                break
            else:
                child = e
        return child

    def _parse_target_namespace(self) -> None:
        """
        XSD 1.1 targetNamespace attribute in elements and attributes declarations.
        """
        if 'targetNamespace' not in self.elem.attrib:
            return

        self._target_namespace = self.elem.attrib['targetNamespace'].strip()
        if 'name' not in self.elem.attrib:
            self.parse_error("attribute 'name' must be present when "
                             "'targetNamespace' attribute is provided")
        if 'form' in self.elem.attrib:
            self.parse_error("attribute 'form' must be absent when "
                             "'targetNamespace' attribute is provided")
        if self._target_namespace != self.schema.target_namespace:
            if self.parent is None:
                self.parse_error("a global %s must have the same namespace as "
                                 "its parent schema" % self.__class__.__name__)

            xsd_type = self.get_parent_type()
            if xsd_type is None or xsd_type.parent is not None:
                pass
            elif xsd_type.derivation != 'restriction' or \
                    getattr(xsd_type.base_type, 'name', None) == XSD_ANY_TYPE:
                self.parse_error("a declaration contained in a global complexType "
                                 "must have the same namespace as its parent schema")

        if self.name is None:
            pass  # pragma: no cover
        elif not self._target_namespace:
            self.name = local_name(self.name)
        else:
            self.name = '{%s}%s' % (self._target_namespace, local_name(self.name))

    @property
    def local_name(self) -> Optional[str]:
        """The local part of the name of the component, or `None` if the name is `None`."""
        return None if self.name is None else local_name(self.name)

    @property
    def qualified_name(self) -> Optional[str]:
        """The name of the component in extended format, or `None` if the name is `None`."""
        return None if self.name is None else get_qname(self.target_namespace, self.name)

    @property
    def prefixed_name(self) -> Optional[str]:
        """The name of the component in prefixed format, or `None` if the name is `None`."""
        return None if self.name is None else get_prefixed_qname(self.name, self.namespaces)

    @property
    def id(self) -> Optional[str]:
        """The ``'id'`` attribute of the component tag, ``None`` if missing."""
        return self.elem.get('id')

    @property
    def validation_attempted(self) -> str:
        return 'full' if self.built else 'partial'

    def build(self) -> None:
        """
        Builds components that are not fully parsed at initialization, like model groups
        or internal local elements in model groups. Otherwise does nothing.
        """

    @property
    def built(self) -> bool:
        raise NotImplementedError()

    def is_matching(self, name: Optional[str], default_namespace: Optional[str] = None,
                    **kwargs: Any) -> bool:
        """
        Returns `True` if the component name is matching the name provided as argument,
        `False` otherwise. For XSD elements the matching is extended to substitutes.

        :param name: a local or fully-qualified name.
        :param default_namespace: used if it's not None and not empty for completing \
        the name argument in case it's a local name.
        :param kwargs: additional options that can be used by certain components.
        """
        if not name:
            return self.name == name
        elif name[0] == '{':
            return self.qualified_name == name
        elif not default_namespace:
            return self.name == name or not self.qualified and self.local_name == name
        else:
            qname = '{%s}%s' % (default_namespace, name)
            return self.qualified_name == qname or not self.qualified and self.local_name == name

    def match(self, name: Optional[str], default_namespace: Optional[str] = None,
              **kwargs: Any) -> Optional['XsdComponent']:
        """
        Returns the component if its name is matching the name provided as argument,
        `None` otherwise.
        """
        return self if self.is_matching(name, default_namespace, **kwargs) else None

    def get_matching_item(self, mapping: MutableMapping[str, Any],
                          ns_prefix: str = 'xmlns',
                          match_local_name: bool = False) -> Optional[Any]:
        """
        If a key is matching component name, returns its value, otherwise returns `None`.
        """
        if self.name is None:
            return None
        elif not self.target_namespace:
            return mapping.get(self.name)
        elif self.qualified_name in mapping:
            return mapping[cast(str, self.qualified_name)]
        elif self.prefixed_name in mapping:
            return mapping[cast(str, self.prefixed_name)]

        # Try a match with other prefixes
        target_namespace = self.target_namespace
        suffix = ':%s' % self.local_name

        for k in filter(lambda x: x.endswith(suffix), mapping):
            prefix = k.split(':')[0]
            if self.namespaces.get(prefix) == target_namespace:
                return mapping[k]

            # Match namespace declaration within value
            ns_declaration = '{}:{}'.format(ns_prefix, prefix)
            try:
                if mapping[k][ns_declaration] == target_namespace:
                    return mapping[k]
            except (KeyError, TypeError):
                pass
        else:
            if match_local_name:
                return mapping.get(self.local_name)  # type: ignore[arg-type]
            return None

    def get_global(self) -> 'XsdComponent':
        """Returns the global XSD component that contains the component instance."""
        if self.parent is None:
            return self
        component = self.parent
        while component is not self:  # pragma: no cover
            if component.parent is None:
                return component
            component = component.parent
        else:
            raise XMLSchemaValueError(f"parent circularity from {self}")  # pragma: no cover

    def get_parent_type(self) -> Optional['XsdType']:
        """
        Returns the nearest XSD type that contains the component instance,
        or `None` if the component doesn't have an XSD type parent.
        """
        component = self.parent
        while component is not self and component is not None:
            if isinstance(component, XsdType):
                return component
            component = component.parent
        return None

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator['XsdComponent']:
        """
        Creates an iterator for XSD subcomponents.

        :param xsd_classes: provide a class or a tuple of classes to iterates over only a \
        specific classes of components.
        """
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self

    def iter_ancestors(self, xsd_classes: ComponentClassType = None)\
            -> Iterator['XsdComponent']:
        """
        Creates an iterator for XSD ancestor components, schema excluded. Stops when the component
        is global or if the ancestor is not an instance of the specified class/classes.

        :param xsd_classes: provide a class or a tuple of classes to iterates over only a \
        specific classes of components.
        """
        ancestor = self
        while True:
            if ancestor.parent is None:
                break
            ancestor = ancestor.parent
            if xsd_classes is not None and not isinstance(ancestor, xsd_classes):
                break
            yield ancestor

    def tostring(self, indent: str = '', max_lines: Optional[int] = None,
                 spaces_for_tab: int = 4) -> Union[str, bytes]:
        """Serializes the XML elements that declare or define the component to a string."""
        return etree_tostring(self.schema_elem, self.namespaces, indent, max_lines, spaces_for_tab) def xsd_builtin_types_factory(
        meta_schema: SchemaType,
        xsd_types: Dict[str, Union[BaseXsdType, Tuple[ElementType, SchemaType]]],
        atomic_builtin_class: Optional[Type[XsdAtomicBuiltin]] = None) -> None:
    """
    Builds the dictionary for XML Schema built-in types mapping.
    """
    atomic_builtin_class = atomic_builtin_class or XsdAtomicBuiltin
    if meta_schema.XSD_VERSION == '1.1':
        builtin_types = XSD_11_BUILTIN_TYPES
        facets_map = XSD_11_FACETS_BUILDERS
    else:
        builtin_types = XSD_10_BUILTIN_TYPES
        facets_map = XSD_10_FACETS_BUILDERS

    #
    # Special builtin types.
    #
    # xs:anyType
    # Ref: https://www.w3.org/TR/xmlschema11-1/#builtin-ctd
    xsd_types[XSD_ANY_TYPE] = meta_schema.create_any_type()

    # xs:anySimpleType
    # Ref: https://www.w3.org/TR/xmlschema11-2/#builtin-stds
    xsd_any_simple_type = xsd_types[XSD_ANY_SIMPLE_TYPE] = XsdSimpleType(
        elem=etree_element(XSD_SIMPLE_TYPE, name=XSD_ANY_SIMPLE_TYPE),
        schema=meta_schema,
        parent=None,
        name=XSD_ANY_SIMPLE_TYPE
    )

    # xs:anyAtomicType
    # Ref: https://www.w3.org/TR/xmlschema11-2/#builtin-stds
    xsd_types[XSD_ANY_ATOMIC_TYPE] = meta_schema.xsd_atomic_restriction_class(
        elem=etree_element(XSD_SIMPLE_TYPE, name=XSD_ANY_ATOMIC_TYPE),
        schema=meta_schema,
        parent=None,
        name=XSD_ANY_ATOMIC_TYPE,
        base_type=xsd_any_simple_type,
    )

    for item in builtin_types:
        item = item.copy()
        name: str = item['name']
        try:
            value = cast(Tuple[ElementType, SchemaType], xsd_types[name])
        except KeyError:
            # If builtin type element is missing create a dummy element. Necessary for the
            # meta-schema XMLSchema.xsd of XSD 1.1, that not includes builtins declarations.
            elem = etree_element(XSD_SIMPLE_TYPE, name=name, id=name)
        else:
            elem, schema = value
            if schema is not meta_schema:
                raise XMLSchemaValueError("loaded entry schema is not the meta-schema!")

        base_type: Union[None, BaseXsdType, Tuple[ElementType, SchemaType]]
        if 'base_type' in item:
            base_type = item['base_type'] = xsd_types[item['base_type']]
        else:
            base_type = None

        facets = item.pop('facets', None)
        builtin_type: XsdAtomicBuiltin = atomic_builtin_class(elem, meta_schema, **item)
        if facets:
            built_facets = builtin_type.facets
            for e in facets:
                try:
                    cls: Any = facets_map[e.tag]
                except AttributeError:
                    built_facets[None] = e
                else:
                    built_facets[e.tag] = cls(e, meta_schema, builtin_type, base_type)
            builtin_type.facets = built_facets

        xsd_types[name] = builtin_type class XsdAssert(XsdComponent, ElementPathMixin[Union['XsdAssert', SchemaElementType]]):
    """
    Class for XSD *assert* constraint definitions.

    ..  <assert
          id = ID
          test = an XPath expression
          xpathDefaultNamespace = (anyURI | (##defaultNamespace | ##targetNamespace | ##local))
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?)
        </assert>
    """
    parent: 'XsdComplexType'
    _ADMITTED_TAGS = {XSD_ASSERT}
    token: Optional[XPathToken] = None
    parser: Optional[XPath2Parser] = None
    path = 'true()'

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: 'XsdComplexType',
                 base_type: 'XsdComplexType') -> None:

        self._xpath_lock = threading.Lock()
        self.base_type = base_type
        super(XsdAssert, self).__init__(elem, schema, parent)

    def __repr__(self) -> str:
        if len(self.path) < 40:
            return '%s(test=%r)' % (self.__class__.__name__, self.path)
        else:
            return '%s(test=%r)' % (self.__class__.__name__, self.path[:37] + '...')

    def __getstate__(self) -> Dict[str, Any]:
        state = self.__dict__.copy()
        state.pop('_xpath_lock', None)
        return state

    def __setstate__(self, state: Any) -> None:
        self.__dict__.update(state)
        self._xpath_lock = threading.Lock()

    def _parse(self) -> None:
        if self.base_type.is_simple():
            self.parse_error("base_type=%r is not a complexType definition" % self.base_type)
        else:
            try:
                self.path = self.elem.attrib['test'].strip()
            except KeyError as err:
                self.parse_error(err)

        if 'xpathDefaultNamespace' in self.elem.attrib:
            self.xpath_default_namespace = self._parse_xpath_default_namespace(self.elem)
        else:
            self.xpath_default_namespace = self.schema.xpath_default_namespace

    @property
    def built(self) -> bool:
        return self.parser is not None and self.token is not None

    def build(self) -> None:
        # Assert requires a schema bound parser because select
        # is on XML elements and with XSD type decoded values
        self.parser = XPath2Parser(
            namespaces=self.namespaces,
            variable_types={'value': self.base_type.sequence_type},
            strict=False,
            default_namespace=self.xpath_default_namespace,
            schema=self.xpath_proxy,
        )

        try:
            self.token = self.parser.parse(self.path)
        except ElementPathError as err:
            self.parse_error(err)
            self.token = self.parser.parse('true()')
        finally:
            if self.parser.variable_types:
                self.parser.variable_types.clear()

    def __call__(self, elem: ElementType,
                 value: Any = None,
                 namespaces: Optional[NamespacesType] = None,
                 source: Optional['XMLResource'] = None,
                 **kwargs: Any) -> Iterator[XMLSchemaValidationError]:

        if self.parser is None or self.token is None:
            raise XMLSchemaNotBuiltError(self, "schema bound parser not set")

        with self._xpath_lock:
            if not self.parser.is_schema_bound() and self.parser.schema:
                self.parser.schema.bind_parser(self.parser)

        if namespaces is None or isinstance(namespaces, dict):
            _namespaces = namespaces
        else:
            _namespaces = dict(namespaces)

        variables = {'value': None if value is None else self.base_type.text_decode(value)}
        if source is not None:
            context = XPathContext(source.root, namespaces=_namespaces,
                                   item=elem, variables=variables)
        else:
            # If validated from a component (could not work with rooted XPath expressions)
            context = XPathContext(elem, variables=variables)

        try:
            if not self.token.evaluate(context):
                yield XMLSchemaValidationError(self, obj=elem, reason="assertion test if false")
        except ElementPathError as err:
            yield XMLSchemaValidationError(self, obj=elem, reason=str(err))

    # For implementing ElementPathMixin
    def __iter__(self) -> Iterator[Union['XsdElement', 'XsdAnyElement']]:
        if isinstance(self.parent.content, XsdGroup):
            yield from self.parent.content.iter_elements()

    @property
    def attrib(self) -> 'XsdAttributeGroup':
        return self.parent.attributes

    @property
    def type(self) -> 'XsdComplexType':
        return self.parent

    @property
    def xpath_proxy(self) -> 'XMLSchemaProxy':
        return XMLSchemaProxy(
            schema=cast(XMLSchemaProtocol, self.schema),
            base_element=cast(ElementProtocol, self)
        ) class XsdNotation(XsdComponent):
    """
    Class for XSD *notation* declarations.

    ..  <notation
          id = ID
          name = NCName
          public = token
          system = anyURI
          {any attributes with non-schema namespace}...>
          Content: (annotation?)
        </notation>
    """
    _ADMITTED_TAGS = {XSD_NOTATION}

    @property
    def built(self) -> bool:
        return True

    def _parse(self) -> None:
        if self.parent is not None:
            self.parse_error("a notation declaration must be global")
        try:
            self.name = get_qname(self.target_namespace, self.elem.attrib['name'])
        except KeyError:
            self.parse_error("a notation must have a 'name' attribute")

        if 'public' not in self.elem.attrib and 'system' not in self.elem.attrib:
            self.parse_error("a notation must have a 'public' or a 'system' attribute")

    @property
    def public(self) -> Optional[str]:
        return self.elem.get('public')

    @property
    def system(self) -> Optional[str]:
        return self.elem.get('system') class XsdIdentity(XsdComponent):
    """
    Common class for XSD identity constraints.

    :ivar selector: the XPath selector of the identity constraint.
    :ivar fields: a list containing the XPath field selectors of the identity constraint.
    """
    name: str
    local_name: str
    prefixed_name: str
    parent: 'XsdElement'
    ref: Optional['XsdIdentity']

    selector = None  # type: XsdSelector
    fields = ()      # type: Union[Tuple[()], List[XsdFieldSelector]]

    # XSD elements bound by selector (for speed-up and for lazy mode)
    elements: Union[Tuple[()], Dict['XsdElement', Optional[IdentityCounterType]]] = ()

    def __init__(self, elem: ElementType, schema: SchemaType,
                 parent: Optional['XsdElement']) -> None:
        super(XsdIdentity, self).__init__(elem, schema, parent)

    def _parse(self) -> None:
        try:
            self.name = get_qname(self.target_namespace, self.elem.attrib['name'])
        except KeyError:
            self.parse_error("missing required attribute 'name'")
            self.name = ''

        for child in self.elem:
            if child.tag == XSD_SELECTOR:
                self.selector = XsdSelector(child, self.schema, self)
                break
        else:
            self.parse_error("missing 'selector' declaration.")

        self.fields = []
        for child in self.elem:
            if child.tag == XSD_FIELD:
                self.fields.append(XsdFieldSelector(child, self.schema, self))

    def build(self) -> None:
        if self.ref is True:  # type: ignore[comparison-overlap]
            try:
                ref = self.maps.identities[self.name]
            except KeyError:
                self.parse_error("unknown identity constraint {!r}".format(self.name))
                return
            else:
                if not isinstance(ref, self.__class__):
                    self.parse_error("attribute 'ref' points to a different kind constraint")
                self.selector = ref.selector
                self.fields = ref.fields
                self.ref = ref

        context = IdentityXPathContext(self.schema, item=self.parent)  # type: ignore

        self.elements = {}
        try:
            for e in self.selector.token.select_results(context):
                if not isinstance(e, XsdComponent) or isinstance(e, XsdAttribute):
                    self.parse_error("selector xpath expression can only select elements")
                elif e.name is not None:
                    if TYPE_CHECKING:
                        assert isinstance(e, XsdElement)  # for mypy checks with Python 3.7
                    self.elements[e] = None
        except AttributeError:
            pass
        else:
            if not self.elements:
                # Try to detect target XSD elements extracting QNames
                # of the leaf elements from the XPath expression and
                # use them to match global elements.

                qname: Any
                for qname in self.selector.token.iter_leaf_elements():
                    xsd_element = self.maps.elements.get(
                        get_extended_qname(qname, self.namespaces)
                    )
                    if xsd_element is not None and \
                            not isinstance(xsd_element, tuple) and \
                            xsd_element not in self.elements:
                        self.elements[xsd_element] = None

    @property
    def built(self) -> bool:
        return not isinstance(self.elements, tuple)

    def get_fields(self, elem: Union[ElementType, 'XsdElement'],
                   namespaces: Optional[NamespacesType] = None,
                   decoders: Optional[Tuple[XsdAttribute, ...]] = None) -> IdentityCounterType:
        """
        Get fields for a schema or instance context element.

        :param elem: an Element or an XsdElement
        :param namespaces: is an optional mapping from namespace prefix to URI.
        :param decoders: context schema fields decoders.
        :return: a tuple with field values. An empty field is replaced by `None`.
        """
        fields: List[IdentityFieldItemType] = []

        if not isinstance(elem, XsdComponent):
            context_class = XPathContext
        else:
            context_class = IdentityXPathContext

        result: Any
        value: Union[AtomicValueType, None]
        for k, field in enumerate(self.fields):
            result = field.token.get_results(context_class(elem))  # type: ignore

            if not result:
                if decoders is not None and decoders[k] is not None:
                    value = decoders[k].value_constraint
                    if value is not None:
                        if decoders[k].type.root_type.name == XSD_QNAME:
                            value = get_extended_qname(value, namespaces)

                        if isinstance(value, list):
                            fields.append(tuple(value))
                        elif isinstance(value, bool):
                            fields.append((value, bool))
                        elif not isinstance(value, float):
                            fields.append(value)
                        elif math.isnan(value):
                            fields.append(('nan', float))
                        else:
                            fields.append((value, float))

                        continue

                if not isinstance(self, XsdKey) or 'ref' in elem.attrib and \
                        self.schema.meta_schema is None and self.schema.XSD_VERSION != '1.0':
                    fields.append(None)
                elif field.target_namespace not in self.maps.namespaces:
                    fields.append(None)
                else:
                    msg = "missing key field {!r} for {!r}"
                    raise XMLSchemaValueError(msg.format(field.path, self))

            elif len(result) == 1:
                if decoders is None or decoders[k] is None:
                    fields.append(result[0])
                else:
                    if decoders[k].type.content_type_label not in ('simple', 'mixed'):
                        raise XMLSchemaTypeError("%r field doesn't have a simple type!" % field)

                    value = decoders[k].data_value(result[0])
                    if decoders[k].type.root_type.name == XSD_QNAME:
                        if isinstance(value, str):
                            value = get_extended_qname(value, namespaces)
                        elif isinstance(value, datatypes.QName):
                            value = value.expanded_name

                    if isinstance(value, list):
                        fields.append(tuple(value))
                    elif isinstance(value, bool):
                        fields.append((value, bool))
                    elif not isinstance(value, float):
                        fields.append(value)
                    elif math.isnan(value):
                        fields.append(('nan', float))
                    else:
                        fields.append((value, float))
            else:
                raise XMLSchemaValueError("%r field selects multiple values!" % field)

        return tuple(fields)

    def get_counter(self, enabled: bool = True) -> 'IdentityCounter':
        return IdentityCounter(self, enabled) class XsdAttribute(XsdComponent, ValidationMixin[str, DecodedValueType]):
    """
    Class for XSD 1.0 *attribute* declarations.

    :ivar type: the XSD simpleType of the attribute.

    ..  <attribute
          default = string
          fixed = string
          form = (qualified | unqualified)
          id = ID
          name = NCName
          ref = QName
          type = QName
          use = (optional | prohibited | required) : optional
          {any attributes with non-schema namespace ...}>
          Content: (annotation?, simpleType?)
        </attribute>
    """
    _ADMITTED_TAGS = {XSD_ATTRIBUTE}

    name: str
    local_name: str
    qualified_name: str
    prefixed_name: str

    type: XsdSimpleType
    copy: Callable[['XsdAttribute'], 'XsdAttribute']

    qualified: bool = False
    default: Optional[str] = None
    fixed: Optional[str] = None
    form: Optional[str] = None
    use: str = 'optional'
    inheritable: bool = False  # For XSD 1.1 attributes, always False for XSD 1.0 attributes.

    def _parse(self) -> None:
        attrib = self.elem.attrib

        if 'use' in attrib and self.parent is not None and \
                attrib['use'] in {'optional', 'prohibited', 'required'}:
            self.use = attrib['use']

        if self._parse_reference():
            try:
                xsd_attribute = self.maps.lookup_attribute(self.name)
            except LookupError:
                self.type = self.any_simple_type
                self.parse_error("unknown attribute {!r}".format(self.name))
            else:
                self.ref = xsd_attribute
                self.type = xsd_attribute.type
                self.qualified = xsd_attribute.qualified
                self.form = xsd_attribute.form

                if xsd_attribute.default is not None and 'default' not in attrib:
                    self.default = xsd_attribute.default

                if xsd_attribute.fixed is not None:
                    if 'fixed' not in attrib:
                        self.fixed = xsd_attribute.fixed
                    elif xsd_attribute.fixed != attrib['fixed']:
                        msg = "referenced attribute has a different fixed value {!r}"
                        self.parse_error(msg.format(xsd_attribute.fixed))

            for attribute in ('form', 'type'):
                if attribute in self.elem.attrib:
                    self.parse_error("attribute {!r} is not allowed when "
                                     "attribute reference is used".format(attribute))
        else:
            if 'form' in attrib:
                self.form = attrib['form']
                if self.parent is not None and self.form == 'qualified':
                    self.qualified = True
            elif self.schema.attribute_form_default == 'qualified':
                self.qualified = True

            try:
                name = attrib['name']
            except KeyError:
                pass
            else:
                if name == 'xmlns':
                    self.parse_error("an attribute name must be different from 'xmlns'")

                if self.parent is None or self.qualified:
                    if self.target_namespace == XSI_NAMESPACE and \
                            name not in {'nil', 'type', 'schemaLocation',
                                         'noNamespaceSchemaLocation'}:
                        self.parse_error("cannot add attributes in %r namespace" % XSI_NAMESPACE)
                    self.name = get_qname(self.target_namespace, name)
                else:
                    self.name = name

            child = self._parse_child_component(self.elem)
            if 'type' in attrib:
                try:
                    type_qname = self.schema.resolve_qname(attrib['type'])
                except (KeyError, ValueError, RuntimeError) as err:
                    self.type = self.any_simple_type
                    self.parse_error(err)
                else:
                    try:
                        self.type = cast(XsdSimpleType, self.maps.lookup_type(type_qname))
                    except LookupError as err:
                        self.type = self.any_simple_type
                        self.parse_error(err)

                    if child is not None and child.tag == XSD_SIMPLE_TYPE:
                        self.parse_error("ambiguous type definition for XSD attribute")

            elif child is not None:
                # No 'type' attribute in declaration, parse for child local simpleType
                self.type = self.schema.simple_type_factory(child, self.schema, self)
            else:
                # Empty declaration means xsdAnySimpleType
                self.type = self.any_simple_type

            if not isinstance(self.type, XsdSimpleType):
                self.type = self.any_simple_type
                self.parse_error("XSD attribute's type must be a simpleType")

        # Check value constraints
        if 'default' in attrib:
            self.default = attrib['default']
            if 'fixed' in attrib:
                self.parse_error("'default' and 'fixed' attributes are mutually exclusive")

            if self.use != 'optional':
                self.parse_error("the attribute 'use' must be 'optional' "
                                 "if the attribute 'default' is present")

            if not self.type.is_valid(self.default):
                msg = "default value {!r} is not compatible with attribute's type"
                self.parse_error(msg.format(self.default))
            elif self.type.is_key() and self.xsd_version == '1.0':
                self.parse_error("xs:ID key attributes cannot have a default value")

        elif 'fixed' in attrib:
            self.fixed = attrib['fixed']
            if not self.type.is_valid(self.fixed):
                msg = "fixed value {!r} is not compatible with attribute's type"
                self.parse_error(msg.format(self.fixed))
            elif self.type.is_key() and self.xsd_version == '1.0':
                self.parse_error("xs:ID key attributes cannot have a fixed value")

    @property
    def built(self) -> bool:
        return True

    @property
    def validation_attempted(self) -> str:
        return 'full'

    @property
    def scope(self) -> str:
        """The scope of the attribute declaration that can be 'global' or 'local'."""
        return 'global' if self.parent is None else 'local'

    @property
    def value_constraint(self) -> Optional[str]:
        """The fixed or the default value if either is defined, `None` otherwise."""
        return self.fixed if self.fixed is not None else self.default

    def is_optional(self) -> bool:
        return self.use == 'optional'

    def is_required(self) -> bool:
        return self.use == 'required'

    def is_prohibited(self) -> bool:
        return self.use == 'prohibited'

    def is_empty(self) -> bool:
        return self.fixed == '' or self.type.is_empty()

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self
        if self.ref is None and self.type.parent is not None:
            yield from self.type.iter_components(xsd_classes)

    def data_value(self, text: str) -> AtomicValueType:
        """Returns the decoded data value of the provided text as XPath fn:data()."""
        return cast(AtomicValueType, self.decode(text, validation='skip'))

    def iter_decode(self, obj: str, validation: str = 'lax', **kwargs: Any) \
            -> IterDecodeType[DecodedValueType]:
        if obj is None and self.default is not None:
            obj = self.default

        if self.type.is_notation():
            if self.type.name == XSD_NOTATION_TYPE:
                msg = "cannot validate against xs:NOTATION directly, " \
                      "only against a subtype with an enumeration facet"
                yield self.validation_error(validation, msg, obj, **kwargs)
            elif not self.type.enumeration:
                msg = "missing enumeration facet in xs:NOTATION subtype"
                yield self.validation_error(validation, msg, obj, **kwargs)

        if self.fixed is not None:
            if obj is None:
                obj = self.fixed
            elif obj != self.fixed and \
                    self.type.text_decode(obj) != self.type.text_decode(self.fixed):
                msg = "attribute {!r} has a fixed value {!r}".format(self.name, self.fixed)
                yield self.validation_error(validation, msg, obj, **kwargs)

        for value in self.type.iter_decode(obj, validation, **kwargs):
            if isinstance(value, XMLSchemaValidationError):
                value.reason = 'attribute {}={!r}: {}'.format(
                    self.prefixed_name, obj, value.reason
                )
                yield value
                continue
            elif 'value_hook' in kwargs:
                yield kwargs['value_hook'](value, self.type)
            elif isinstance(value, (int, float, list)) or value is None:
                yield value
            elif isinstance(value, str):
                if value.startswith('{') and self.type.is_qname():
                    yield obj
                else:
                    yield value
            elif isinstance(value, Decimal):
                try:
                    yield kwargs['decimal_type'](value)
                except (KeyError, TypeError):
                    yield value
            elif isinstance(value, (AbstractDateTime, Duration)):
                yield value if kwargs.get('datetime_types') else obj.strip()
            elif isinstance(value, AbstractBinary) and not kwargs.get('binary_types'):
                yield str(value)
            else:
                yield value
            break  # pragma: no cover

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[Union[EncodedValueType]]:
        yield from self.type.iter_encode(obj, validation, **kwargs) class XsdAttributeGroup(
        MutableMapping[Optional[str], Union[XsdAttribute, XsdAnyAttribute]], XsdComponent,
        ValidationMixin[MutableMapping[str, str], List[Tuple[str, Any]]]
):
    """
    Class for XSD *attributeGroup* definitions.

    .. <attributeGroup
          id = ID
          name = NCName
          ref = QName
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?))
        </attributeGroup>
    """
    _ADMITTED_TAGS = {
        XSD_ATTRIBUTE_GROUP, XSD_COMPLEX_TYPE, XSD_RESTRICTION, XSD_EXTENSION,
        XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE
    }
    copy: Callable[['XsdAttributeGroup'], 'XsdAttributeGroup']

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: Optional[XsdComponent] = None,
                 derivation: Optional[str] = None,
                 base_attributes: Optional['XsdAttributeGroup'] = None) -> None:

        self._attribute_group: Dict[Optional[str], Union[XsdAttribute, XsdAnyAttribute]] = {}
        self.derivation = derivation
        self.base_attributes = base_attributes
        XsdComponent.__init__(self, elem, schema, parent)

    def __repr__(self) -> str:
        if self.name is not None:
            return '%s(name=%r)' % (self.__class__.__name__, self.name)
        elif self:
            names = [a if a.name is None else a.name for a in self.values()]
            return '%s(%r)' % (self.__class__.__name__, names)
        else:
            return '%s()' % self.__class__.__name__

    # Implementation of abstract methods
    def __getitem__(self, key: Optional[str]) -> Union[XsdAttribute, XsdAnyAttribute]:
        return self._attribute_group[key]

    def __setitem__(self, key: Optional[str],
                    value: Union[XsdAttribute, XsdAnyAttribute]) -> None:
        if value.name != key:
            raise XMLSchemaValueError("%r name and key %r mismatch" % (value.name, key))
        self._attribute_group[key] = value

    def __delitem__(self, key: Optional[str]) -> None:
        del self._attribute_group[key]

    def __iter__(self) -> Iterator[Optional[str]]:
        if None in self._attribute_group:
            # Put AnyAttribute ('None' key) at the end of iteration
            yield from sorted(self._attribute_group, key=lambda x: (x is None, x))
        else:
            yield from self._attribute_group

    def __len__(self) -> int:
        return len(self._attribute_group)

    def _parse(self) -> None:
        if self.elem.tag == XSD_ATTRIBUTE_GROUP:
            if self.parent is not None:
                return  # Skip parsing dummy instances
            try:
                self.name = get_qname(self.target_namespace, self.elem.attrib['name'])
            except KeyError:
                return
            else:
                if self.schema.default_attributes == self.name and self.xsd_version > '1.0':
                    self.schema.default_attributes = self

        any_attribute = None
        attribute_group_refs = []
        attributes: Dict[Optional[str], Union[XsdAttribute, XsdAnyAttribute]] = {}

        for child in self.elem:
            if child.tag == XSD_ANNOTATION or callable(child.tag):
                continue  # pragma: no cover
            elif any_attribute is not None:
                if child.tag == XSD_ANY_ATTRIBUTE:
                    self.parse_error("more anyAttribute declarations in the same attribute group")
                elif child.tag != XSD_ASSERT:
                    self.parse_error("another declaration after anyAttribute")

            elif child.tag == XSD_ANY_ATTRIBUTE:
                any_attribute = self.schema.xsd_any_attribute_class(child, self.schema, self)
                if None in attributes:
                    attributes[None] = attr = attributes[None].copy()
                    assert isinstance(attr, XsdAnyAttribute)
                    attr.intersection(any_attribute)
                else:
                    attributes[None] = any_attribute

            elif child.tag == XSD_ATTRIBUTE:
                attribute = self.schema.xsd_attribute_class(child, self.schema, self)
                if attribute.name in attributes:
                    self.parse_error("multiple declaration for attribute "
                                     "{!r}".format(attribute.name))
                elif attribute.use != 'prohibited' or self.elem.tag != XSD_ATTRIBUTE_GROUP:
                    attributes[attribute.name] = attribute

            elif child.tag == XSD_ATTRIBUTE_GROUP:
                try:
                    ref = child.attrib['ref']
                except KeyError:
                    self.parse_error("the attribute 'ref' is required "
                                     "in a local attributeGroup")
                    continue

                try:
                    attribute_group_qname = self.schema.resolve_qname(ref)
                except (KeyError, ValueError, RuntimeError) as err:
                    self.parse_error(err)
                else:
                    if attribute_group_qname in attribute_group_refs:
                        self.parse_error("duplicated attributeGroup %r" % ref)
                    elif self.redefine is not None:
                        if attribute_group_qname == self.name:
                            if attribute_group_refs:
                                self.parse_error("in a redefinition the reference "
                                                 "to itself must be the first")
                            attribute_group_refs.append(attribute_group_qname)
                            attributes.update(self._attribute_group)
                            continue
                        elif not attribute_group_refs:
                            # May be an attributeGroup restriction with a ref to another group
                            if not any(e.tag == XSD_ATTRIBUTE_GROUP and ref == e.get('ref')
                                       for e in self.redefine.elem):
                                self.parse_error("attributeGroup ref=%r is not "
                                                 "in the redefined group" % ref)
                    elif attribute_group_qname == self.name and self.xsd_version == '1.0':
                        self.parse_error("Circular attribute groups not allowed in XSD 1.0")
                    attribute_group_refs.append(attribute_group_qname)

                    try:
                        ref_attributes = self.maps.lookup_attribute_group(attribute_group_qname)
                    except LookupError:
                        self.parse_error("unknown attribute group %r" % child.attrib['ref'])
                    else:
                        if not isinstance(ref_attributes, tuple):
                            for name, base_attr in ref_attributes.items():
                                if name not in attributes:
                                    attributes[name] = base_attr
                                elif name is not None:
                                    if base_attr is not attributes[name]:
                                        self.parse_error(
                                            f"multiple declaration of attribute {name!r}"
                                        )
                                else:
                                    assert isinstance(base_attr, XsdAnyAttribute)
                                    attributes[None] = attr = attributes[None].copy()
                                    assert isinstance(attr, XsdAnyAttribute)
                                    attr.intersection(base_attr)

                        elif self.xsd_version == '1.0':
                            self.parse_error(
                                "Circular reference found between attribute groups "
                                "{!r} and {!r}".format(self.name, attribute_group_qname)
                            )

            elif self.name is not None:
                self.parse_error("(attribute | attributeGroup) expected, found %r." % child)

        # Check and copy base attributes
        if self.base_attributes is not None:
            wildcard = self.base_attributes.get(None)
            for name, attr in attributes.items():
                if name not in self.base_attributes:
                    if self.derivation != 'restriction':
                        continue
                    elif wildcard is None or not wildcard.is_matching(name, self.default_namespace):
                        self.parse_error("Unexpected attribute %r in restriction" % name)
                    continue

                base_attr = self.base_attributes[name]

                if isinstance(attr, XsdAnyAttribute):
                    assert name is None, "name key resolves to an xs:anyAttribute"
                    assert isinstance(base_attr, XsdAnyAttribute), "invalid base attribute"

                    if self.derivation == 'extension':
                        try:
                            attr.union(base_attr)
                        except ValueError as err:
                            self.parse_error(err)
                    elif not attr.is_restriction(base_attr):
                        self.parse_error("Attribute wildcard is not a restriction "
                                         "of the base wildcard")
                    continue

                assert name is not None, "None key resolves to an xs:attribute"
                assert isinstance(base_attr, XsdAttribute), "invalid base attribute"

                if self.derivation == 'restriction' and attr.type.name != XSD_ANY_SIMPLE_TYPE and \
                        not attr.type.is_derived(base_attr.type, 'restriction'):
                    self.parse_error("Attribute type is not a restriction "
                                     "of the base attribute type")
                if base_attr.use != 'optional' and attr.use == 'optional' or \
                        base_attr.use == 'required' and attr.use != 'required':
                    self.parse_error("Attribute %r: unmatched attribute use in restriction" % name)
                if base_attr.fixed is not None:
                    if attr.fixed is None or attr.type.normalize(attr.fixed) != \
                            base_attr.type.normalize(base_attr.fixed):
                        self.parse_error("Attribute %r: derived attribute "
                                         "has a different fixed value" % name)
                if base_attr.inheritable is not attr.inheritable:
                    msg = "Attribute %r: attribute 'inheritable' value change in restriction"
                    self.parse_error(msg % name)

            if self.redefine is not None:
                pass  # In case of redefinition do not copy base attributes
            else:
                self._attribute_group.update(self.base_attributes.items())

        elif self.redefine is not None and not attribute_group_refs:
            for name, attr in self._attribute_group.items():
                if name is None:
                    continue
                elif name not in attributes:
                    if attr.use == 'required':
                        self.parse_error("Missing required attribute %r in "
                                         "redefinition restriction" % name)
                    continue
                if attr.use != 'optional' and attributes[name].use != attr.use:
                    self.parse_error("Attribute %r: unmatched attribute use in redefinition" % name)
                if attr.fixed is not None and attributes[name].fixed is None:
                    self.parse_error("Attribute %r: redefinition remove fixed constraint" % name)

            pos = 0
            keys = list(self._attribute_group.keys())
            for name in attributes:
                try:
                    next_pos = keys.index(name)
                except ValueError:
                    self.parse_error("Redefinition restriction contains "
                                     "additional attribute %r" % name)
                else:
                    if next_pos < pos:
                        self.parse_error("Wrong attribute order in redefinition restriction")
                        break
                    pos = next_pos
            self.clear()

        self._attribute_group.update(attributes)
        if None in self._attribute_group and None not in attributes \
                and self.derivation == 'restriction':
            wildcard = self._attribute_group[None].copy()
            wildcard.namespace = wildcard.not_namespace = wildcard.not_qname = ()
            self._attribute_group[None] = wildcard

        if self.xsd_version == '1.0':
            has_key = False
            for attr in self._attribute_group.values():
                if attr.type is not None and attr.type.is_key():
                    if has_key:
                        self.parse_error("multiple ID attributes not allowed for XSD 1.0")
                        break
                    has_key = True

        elif self.parent is None and self.schema.default_attributes == self.name:
            self.schema.default_attributes = self

    @property
    def built(self) -> bool:
        return True

    def parse_error(self, error: Union[str, Exception],
                    elem: Optional[ElementType] = None,
                    validation: Optional[str] = None) -> None:
        if self.parent is None:
            super().parse_error(error, elem, validation)
        else:
            self.parent.parse_error(error, elem, validation)

    def iter_required(self) -> Iterator[str]:
        for k, v in self._attribute_group.items():
            if isinstance(v, XsdAttribute) and k is not None:
                if v.use == 'required':
                    yield k

    def iter_value_constraints(self, use_defaults: bool = True) -> Iterator[Tuple[str, str]]:
        if use_defaults:
            for k, v in self._attribute_group.items():
                if v.fixed is not None and k:
                    yield k, v.fixed
                elif v.default is not None and k:
                    yield k, v.default
        else:
            for k, v in self._attribute_group.items():
                if v.fixed is not None and k:
                    yield k, v.fixed

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self

        for attr in self.values():
            if attr.parent is not None:
                yield from attr.iter_components(xsd_classes)

    def iter_decode(self, obj: MutableMapping[str, str], validation: str = 'lax',
                    **kwargs: Any) -> IterDecodeType[List[Tuple[str, Any]]]:
        if not obj and not self:
            return

        for name in filter(lambda x: x not in obj, self.iter_required()):
            reason = "missing required attribute {!r}".format(name)
            yield self.validation_error(validation, reason, obj, **kwargs)

        kwargs['level'] = kwargs.get('level', 0) + 1
        try:
            use_defaults = kwargs['use_defaults']
        except KeyError:
            use_defaults = True

        additional_attrs = [
            (k, v) for k, v in self.iter_value_constraints(use_defaults) if k not in obj
        ]
        if additional_attrs:
            obj = {k: v for k, v in obj.items()}
            obj.update(additional_attrs)

        if self.xsd_version == '1.0':
            kwargs['id_list'] = []

        filler = kwargs.get('filler')
        result_list: List[Tuple[str, DecodedValueType]] = []

        value: Any
        for name, value in obj.items():
            try:
                xsd_attribute = self._attribute_group[name]
            except KeyError:
                if get_namespace(name) == XSI_NAMESPACE:
                    try:
                        xsd_attribute = self.maps.lookup_attribute(name)
                    except LookupError:
                        try:
                            xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                            value = (name, value)
                        except KeyError:
                            reason = "%r is not an attribute of the XSI namespace." % name
                            yield self.validation_error(validation, reason, obj, **kwargs)
                            continue
                else:
                    try:
                        xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                        value = (name, value)
                    except KeyError:
                        reason = "%r attribute not allowed for element." % name
                        yield self.validation_error(validation, reason, obj, **kwargs)
                        continue
            else:
                if xsd_attribute.use == 'prohibited' and \
                        (None not in self or not self._attribute_group[None].is_matching(name)):
                    reason = "use of attribute %r is prohibited" % name
                    yield self.validation_error(validation, reason, obj, **kwargs)

            for result in xsd_attribute.iter_decode(value, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    yield result
                elif result is None and filler is not None:
                    result_list.append((name, filler(xsd_attribute)))
                    break
                else:
                    result_list.append((name, result))
                    break

        if kwargs.get('fill_missing'):
            if filler is None:
                result_list.extend((k, None) for k in self._attribute_group
                                   if k is not None and k not in obj)
            else:
                result_list.extend((k, filler(v)) for k, v in self._attribute_group.items()
                                   if k is not None and k not in obj)

        yield result_list

    def iter_encode(self, obj: MutableMapping[str, Any], validation: str = 'lax',
                    **kwargs: Any) -> IterEncodeType[List[Tuple[str, Union[str, List[str]]]]]:
        if not obj and not self:
            return

        for name in filter(lambda x: x not in obj, self.iter_required()):
            reason = "missing required attribute {!r}".format(name)
            yield self.validation_error(validation, reason, obj, **kwargs)

        try:
            use_defaults = kwargs['use_defaults']
        except KeyError:
            use_defaults = True

        result_list = []
        for name, value in obj.items():
            try:
                xsd_attribute = self._attribute_group[name]
            except KeyError:
                namespace = get_namespace(name) or self.target_namespace
                if namespace == XSI_NAMESPACE:
                    try:
                        xsd_attribute = self.maps.lookup_attribute(name)
                    except LookupError:
                        try:
                            xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                            value = (name, value)
                        except KeyError:
                            reason = "%r is not an attribute of the XSI namespace." % name
                            yield self.validation_error(validation, reason, obj, **kwargs)
                            continue
                else:
                    try:
                        xsd_attribute = self._attribute_group[None]  # None == anyAttribute
                        value = (name, value)
                    except KeyError:
                        reason = "%r attribute not allowed for element." % name
                        yield self.validation_error(validation, reason, obj, **kwargs)
                        continue

            for result in xsd_attribute.iter_encode(value, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    yield result
                else:
                    if result is not None:
                        result_list.append((name, result))
                    break

        result_list.extend(
            (k, v) for k, v in self.iter_value_constraints(use_defaults) if k not in obj
        )
        yield result_list class XsdSimpleType(XsdType, ValidationMixin[Union[str, bytes], DecodedValueType]):
    """
    Base class for simpleTypes definitions. Generally used only for
    instances of xs:anySimpleType.

    ..  <simpleType
          final = (#all | List of (list | union | restriction | extension))
          id = ID
          name = NCName
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (restriction | list | union))
        </simpleType>
    """
    _special_types = {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE}
    _ADMITTED_TAGS = {XSD_SIMPLE_TYPE}
    copy: Callable[['XsdSimpleType'], 'XsdSimpleType']

    block: str = ''
    min_length: Optional[int] = None
    max_length: Optional[int] = None
    white_space: Optional[str] = None
    patterns = None
    validators: Union[Tuple[()], List[Union[XsdFacet, Callable[[Any], None]]]] = ()
    allow_empty = True
    facets: Dict[Optional[str], FacetsValueType]

    python_type: PythonTypeClasses
    instance_types: Union[PythonTypeClasses, Tuple[PythonTypeClasses]]

    # Unicode string as default datatype for XSD simple types
    python_type = instance_types = to_python = from_python = str

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: Optional[XsdComponent] = None,
                 name: Optional[str] = None,
                 facets: Optional[Dict[Optional[str], FacetsValueType]] = None) -> None:

        super(XsdSimpleType, self).__init__(elem, schema, parent, name)
        if not hasattr(self, 'facets'):
            self.facets = facets if facets is not None else {}

    def __setattr__(self, name: str, value: Any) -> None:
        super(XsdSimpleType, self).__setattr__(name, value)
        if name == 'facets':
            if not isinstance(self, XsdAtomicBuiltin):
                self._parse_facets(value)

            if self.min_length:
                self.allow_empty = False

            white_space = getattr(self.get_facet(XSD_WHITE_SPACE), 'value', None)
            if white_space is not None:
                self.white_space = white_space

            patterns = self.get_facet(XSD_PATTERN)
            if isinstance(patterns, XsdPatternFacets):
                self.patterns = patterns
                if all(p.match('') is None for p in patterns.patterns):
                    self.allow_empty = False

            enumeration = self.get_facet(XSD_ENUMERATION)
            if isinstance(enumeration, XsdEnumerationFacets) \
                    and '' not in enumeration.enumeration:
                self.allow_empty = False

            if value:
                validators: List[Union[XsdFacet, Callable[[Any], None]]]
                if None in value:
                    validators = [value[None]]  # Use only the validator function!
                else:
                    validators = [v for k, v in value.items()
                                  if k not in {XSD_WHITE_SPACE, XSD_PATTERN, XSD_ASSERTION}]
                if XSD_ASSERTION in value:
                    assertions: Union[XsdAssertionFacet, List[XsdAssertionFacet]]
                    assertions = value[XSD_ASSERTION]
                    if isinstance(assertions, list):
                        validators.extend(assertions)
                    else:
                        validators.append(assertions)
                if validators:
                    self.validators = validators

    def _parse_facets(self, facets: Any) -> None:
        base_type: Any

        if facets and self.base_type is not None:
            if isinstance(self.base_type, XsdSimpleType):
                if self.base_type.name == XSD_ANY_SIMPLE_TYPE:
                    self.parse_error(
                        "facets not allowed for a direct derivation of xs:anySimpleType"
                    )
            elif self.base_type.has_simple_content():
                if self.base_type.content.name == XSD_ANY_SIMPLE_TYPE:
                    self.parse_error(
                        "facets not allowed for a direct content derivation of xs:anySimpleType"
                    )

        # Checks the applicability of the facets
        if any(k not in self.admitted_facets for k in facets if k is not None):
            reason = "one or more facets are not applicable, admitted set is %r:"
            self.parse_error(reason % {local_name(e) for e in self.admitted_facets if e})

        # Check group base_type
        base_type = {t.base_type for t in facets.values() if isinstance(t, XsdFacet)}
        if len(base_type) > 1:
            self.parse_error("facet group must have the same base_type: %r" % base_type)
        base_type = base_type.pop() if base_type else None

        # Checks length based facets
        length = getattr(facets.get(XSD_LENGTH), 'value', None)
        min_length = getattr(facets.get(XSD_MIN_LENGTH), 'value', None)
        max_length = getattr(facets.get(XSD_MAX_LENGTH), 'value', None)
        if length is not None:
            if length < 0:
                self.parse_error("'length' value must be non negative integer")
            if min_length is not None:
                if min_length > length:
                    self.parse_error("'minLength' value must be less or equal to 'length'")
                min_length_facet = base_type.get_facet(XSD_MIN_LENGTH)
                length_facet = base_type.get_facet(XSD_LENGTH)
                if (min_length_facet is None
                        or (length_facet is not None
                            and length_facet.base_type == min_length_facet.base_type)):
                    self.parse_error("cannot specify both 'length' and 'minLength'")
            if max_length is not None:
                if max_length < length:
                    self.parse_error("'maxLength' value must be greater or equal to 'length'")
                max_length_facet = base_type.get_facet(XSD_MAX_LENGTH)
                length_facet = base_type.get_facet(XSD_LENGTH)
                if max_length_facet is None \
                        or (length_facet is not None
                            and length_facet.base_type == max_length_facet.base_type):
                    self.parse_error("cannot specify both 'length' and 'maxLength'")
            min_length = max_length = length
        elif min_length is not None or max_length is not None:
            min_length_facet = base_type.get_facet(XSD_MIN_LENGTH)
            max_length_facet = base_type.get_facet(XSD_MAX_LENGTH)
            if min_length is not None:
                if min_length < 0:
                    self.parse_error("'minLength' value must be non negative integer")
                if max_length is not None and max_length < min_length:
                    self.parse_error("'maxLength' value is lesser than 'minLength'")
                if min_length_facet is not None and min_length_facet.value > min_length:
                    self.parse_error("'minLength' has a lesser value than parent")
                if max_length_facet is not None and min_length > max_length_facet.value:
                    self.parse_error("'minLength' has a greater value than parent 'maxLength'")

            if max_length is not None:
                if max_length < 0:
                    self.parse_error("'maxLength' value mu  st be non negative integer")
                if min_length_facet is not None and min_length_facet.value > max_length:
                    self.parse_error("'maxLength' has a lesser value than parent 'minLength'")
                if max_length_facet is not None and max_length > max_length_facet.value:
                    self.parse_error("'maxLength' has a greater value than parent")

        # Checks min/max values
        min_inclusive = getattr(facets.get(XSD_MIN_INCLUSIVE), 'value', None)
        min_exclusive = getattr(facets.get(XSD_MIN_EXCLUSIVE), 'value', None)
        max_inclusive = getattr(facets.get(XSD_MAX_INCLUSIVE), 'value', None)
        max_exclusive = getattr(facets.get(XSD_MAX_EXCLUSIVE), 'value', None)

        if min_inclusive is not None:
            if min_exclusive is not None:
                self.parse_error("cannot specify both 'minInclusive' and 'minExclusive")
            if max_inclusive is not None and min_inclusive > max_inclusive:
                self.parse_error("'minInclusive' must be less or equal to 'maxInclusive'")
            elif max_exclusive is not None and min_inclusive >= max_exclusive:
                self.parse_error("'minInclusive' must be lesser than 'maxExclusive'")

        elif min_exclusive is not None:
            if max_inclusive is not None and min_exclusive >= max_inclusive:
                self.parse_error("'minExclusive' must be lesser than 'maxInclusive'")
            elif max_exclusive is not None and min_exclusive > max_exclusive:
                self.parse_error("'minExclusive' must be less or equal to 'maxExclusive'")

        if max_inclusive is not None and max_exclusive is not None:
            self.parse_error("cannot specify both 'maxInclusive' and 'maxExclusive")

        # Checks fraction digits
        if XSD_TOTAL_DIGITS in facets:
            if XSD_FRACTION_DIGITS in facets and \
                    facets[XSD_TOTAL_DIGITS].value < facets[XSD_FRACTION_DIGITS].value:
                self.parse_error("fractionDigits facet value cannot be lesser than the "
                                 "value of totalDigits facet")
            total_digits = base_type.get_facet(XSD_TOTAL_DIGITS)
            if total_digits is not None and total_digits.value < facets[XSD_TOTAL_DIGITS].value:
                self.parse_error("totalDigits facet value cannot be greater than "
                                 "the value of the same facet in the base type")

        # Checks XSD 1.1 facets
        if XSD_EXPLICIT_TIMEZONE in facets:
            explicit_tz_facet = base_type.get_facet(XSD_EXPLICIT_TIMEZONE)
            if explicit_tz_facet and explicit_tz_facet.value in ('prohibited', 'required') \
                    and facets[XSD_EXPLICIT_TIMEZONE].value != explicit_tz_facet.value:
                self.parse_error("the explicitTimezone facet value cannot be changed if the base "
                                 "type has the same facet with value %r" % explicit_tz_facet.value)

        self.min_length = min_length
        self.max_length = max_length

    @property
    def variety(self) -> Optional[str]:
        return None

    @property
    def simple_type(self) -> 'XsdSimpleType':
        return self

    @property
    def min_value(self) -> Optional[AtomicValueType]:
        min_exclusive: Optional['AtomicValueType']
        min_inclusive: Optional['AtomicValueType']
        min_exclusive = cast(
            Optional['AtomicValueType'],
            getattr(self.get_facet(XSD_MIN_EXCLUSIVE), 'value', None)
        )
        min_inclusive = cast(
            Optional['AtomicValueType'],
            getattr(self.get_facet(XSD_MIN_INCLUSIVE), 'value', None)
        )

        if min_exclusive is None:
            return min_inclusive
        elif min_inclusive is None:
            return min_exclusive
        elif min_inclusive <= min_exclusive:  # type: ignore[operator]
            return min_exclusive
        else:
            return min_inclusive

    @property
    def max_value(self) -> Optional[AtomicValueType]:
        max_exclusive: Optional['AtomicValueType']
        max_inclusive: Optional['AtomicValueType']
        max_exclusive = cast(
            Optional['AtomicValueType'],
            getattr(self.get_facet(XSD_MAX_EXCLUSIVE), 'value', None)
        )
        max_inclusive = cast(
            Optional['AtomicValueType'],
            getattr(self.get_facet(XSD_MAX_INCLUSIVE), 'value', None)
        )

        if max_exclusive is None:
            return max_inclusive
        elif max_inclusive is None:
            return max_exclusive
        elif max_inclusive >= max_exclusive:  # type: ignore[operator]
            return max_exclusive
        else:
            return max_inclusive

    @property
    def enumeration(self) -> Optional[List[Optional[AtomicValueType]]]:
        enumeration = self.get_facet(XSD_ENUMERATION)
        if isinstance(enumeration, XsdEnumerationFacets):
            return enumeration.enumeration
        return None

    @property
    def admitted_facets(self) -> Set[str]:
        return XSD_10_FACETS if self.xsd_version == '1.0' else XSD_11_FACETS

    @property
    def built(self) -> bool:
        return True

    @staticmethod
    def is_simple() -> bool:
        return True

    @staticmethod
    def is_complex() -> bool:
        return False

    @property
    def content_type_label(self) -> str:
        return 'empty' if self.max_length == 0 else 'simple'

    @property
    def sequence_type(self) -> str:
        if self.is_empty():
            return 'empty-sequence()'

        root_type = self.root_type
        if root_type.name is not None:
            sequence_type = cast(str, root_type.prefixed_name)
        else:
            sequence_type = get_prefixed_qname(XSD_UNTYPED_ATOMIC, self.namespaces)

        if not self.is_list():
            return sequence_type
        elif self.is_emptiable():
            return '{}*'.format(sequence_type)
        else:
            return '{}+'.format(sequence_type)

    def is_empty(self) -> bool:
        return self.max_length == 0 or \
            self.enumeration is not None and all(v == '' for v in self.enumeration)

    def is_emptiable(self) -> bool:
        return self.allow_empty

    def has_simple_content(self) -> bool:
        return self.max_length != 0

    def has_complex_content(self) -> bool:
        return False

    def has_mixed_content(self) -> bool:
        return False

    def is_element_only(self) -> bool:
        return False

    def is_derived(self, other: Union[BaseXsdType, Tuple[ElementType, SchemaType]],
                   derivation: Optional[str] = None) -> bool:
        if self is other:
            return True
        elif isinstance(other, tuple):
            other[1].parse_error(f"global type {other[0].tag!r} is not built")
            return False
        elif derivation and self.derivation and derivation != self.derivation:
            return False
        elif other.name in self._special_types:
            return derivation != 'extension'
        elif self.base_type is other:
            return True
        elif self.base_type is None:
            if isinstance(other, XsdUnion):
                return any(self.is_derived(m, derivation) for m in other.member_types)
            return False
        elif self.base_type.is_complex():
            if not self.base_type.has_simple_content():
                return False
            return self.base_type.content.is_derived(other, derivation)  # type: ignore
        elif isinstance(other, XsdUnion):
            return any(self.is_derived(m, derivation) for m in other.member_types)
        else:
            return self.base_type.is_derived(other, derivation)

    def is_dynamic_consistent(self, other: BaseXsdType) -> bool:
        return other.name in {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE} or self.is_derived(other) or \
            isinstance(other, XsdUnion) and any(self.is_derived(mt) for mt in other.member_types)

    def normalize(self, text: Union[str, bytes]) -> str:
        """
        Normalize and restrict value-space with pre-lexical and lexical facets.

        :param text: text string encoded value.
        :return: a normalized string.
        """
        if isinstance(text, bytes):
            text = text.decode('utf-8')
        elif not isinstance(text, str):
            raise XMLSchemaValueError('argument is not a string: %r' % text)

        if self.white_space == 'replace':
            return self._REGEX_SPACE.sub(' ', text)
        elif self.white_space == 'collapse':
            return self._REGEX_SPACES.sub(' ', text).strip()
        else:
            return text

    def text_decode(self, text: str) -> AtomicValueType:
        return cast(AtomicValueType, self.decode(text, validation='skip'))

    def iter_decode(self, obj: Union[str, bytes], validation: str = 'lax',
                    **kwargs: Any) -> IterDecodeType[DecodedValueType]:
        if obj is None:
            yield obj
        else:
            text = self.normalize(obj)
            if self.patterns is not None:
                try:
                    self.patterns(text)
                except XMLSchemaValidationError as err:
                    yield err

            for validator in self.validators:
                try:
                    validator(text)
                except XMLSchemaValidationError as err:
                    yield err

            yield text

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[EncodedValueType]:
        if not isinstance(obj, (str, bytes)):
            reason = "a {!r} or {!r} object required".format(str, bytes)
            yield XMLSchemaEncodeError(self, obj, str, reason)
        else:
            text = self.normalize(obj)
            if self.patterns is not None:
                try:
                    self.patterns(text)
                except XMLSchemaValidationError as err:
                    yield err

            for validator in self.validators:
                try:
                    validator(text)
                except XMLSchemaValidationError as err:
                    yield err

            yield text

    def get_facet(self, tag: str) -> Optional[FacetsValueType]:
        return self.facets.get(tag) class XsdAtomicRestriction(XsdAtomic):
    """
    Class for XSD 1.0 atomic simpleType and complexType's simpleContent restrictions.

    ..  <restriction
          base = QName
          id = ID
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (simpleType?, (minExclusive | minInclusive | maxExclusive |
          maxInclusive | totalDigits | fractionDigits | length | minLength | maxLength |
          enumeration | whiteSpace | pattern)*))
        </restriction>
    """
    parent: 'XsdSimpleType'
    base_type: BaseXsdType
    derivation = 'restriction'
    _FACETS_BUILDERS = XSD_10_FACETS_BUILDERS
    _CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ANY_ATTRIBUTE}

    def __setattr__(self, name: str, value: Any) -> None:
        if name == 'elem' and value is not None:
            if self.name != XSD_ANY_ATOMIC_TYPE and value.tag != XSD_RESTRICTION:
                if not (value.tag == XSD_SIMPLE_TYPE and value.get('name') is not None):
                    raise XMLSchemaValueError(
                        "an xs:restriction definition required for %r." % self
                    )
        super(XsdAtomicRestriction, self).__setattr__(name, value)

    def _parse(self) -> None:
        elem = self.elem
        if elem.get('name') == XSD_ANY_ATOMIC_TYPE:
            return  # skip special type xs:anyAtomicType
        elif elem.tag == XSD_SIMPLE_TYPE and elem.get('name') is not None:
            # Global simpleType with internal restriction
            elem = cast(ElementType, self._parse_child_component(elem))

        if self.name is not None and self.parent is not None:
            self.parse_error("'name' attribute in a local simpleType definition")

        base_type: Any = None
        facets: Any = {}
        has_attributes = False
        has_simple_type_child = False

        if 'base' in elem.attrib:
            try:
                base_qname = self.schema.resolve_qname(elem.attrib['base'])
            except (KeyError, ValueError, RuntimeError) as err:
                self.parse_error(err)
                base_type = self.any_atomic_type
            else:
                if base_qname == self.name:
                    if self.redefine is None:
                        self.parse_error("wrong definition with self-reference")
                        base_type = self.any_atomic_type
                    else:
                        base_type = self.base_type
                else:
                    if self.redefine is not None:
                        self.parse_error("wrong redefinition without self-reference")

                    try:
                        base_type = self.maps.lookup_type(base_qname)
                    except KeyError:
                        self.parse_error("unknown type %r" % elem.attrib['base'])
                        base_type = self.any_atomic_type
                    except XMLSchemaParseError as err:
                        self.parse_error(err)
                        base_type = self.any_atomic_type
                    else:
                        if isinstance(base_type, tuple):
                            msg = "circularity definition between {!r} and {!r}"
                            self.parse_error(msg.format(self, base_qname))
                            base_type = self.any_atomic_type

            if base_type.is_simple() and base_type.name == XSD_ANY_SIMPLE_TYPE:
                self.parse_error("wrong base type {!r}, an atomic type required")
            elif base_type.is_complex():
                if base_type.mixed and base_type.is_emptiable():
                    child = self._parse_child_component(elem, strict=False)
                    if child is None:
                        self.parse_error("an xs:simpleType definition expected")
                    elif child.tag != XSD_SIMPLE_TYPE:
                        # See: "http://www.w3.org/TR/xmlschema-2/#element-restriction"
                        self.parse_error(
                            "when a complexType with simpleContent restricts a complexType "
                            "with mixed and with emptiable content then a simpleType child "
                            "declaration is required."
                        )
                elif self.parent is None or self.parent.is_simple():
                    self.parse_error(
                        "simpleType restriction of %r is not allowed" % base_type
                    )

        for child in elem:
            if child.tag == XSD_ANNOTATION or callable(child.tag):
                continue
            elif child.tag in self._CONTENT_TAIL_TAGS:
                has_attributes = True  # only if it's a complexType restriction
            elif has_attributes:
                self.parse_error("unexpected tag after attribute declarations")
            elif child.tag == XSD_SIMPLE_TYPE:
                # Case of simpleType declaration inside a restriction
                if has_simple_type_child:
                    self.parse_error("duplicated simpleType declaration")

                if base_type is None:
                    try:
                        base_type = self.schema.simple_type_factory(child, parent=self)
                    except XMLSchemaParseError as err:
                        self.parse_error(err, child)
                        base_type = self.any_simple_type
                elif base_type.is_complex():
                    if base_type.admit_simple_restriction():
                        base_type = self.schema.xsd_complex_type_class(
                            elem=elem,
                            schema=self.schema,
                            parent=self,
                            content=self.schema.simple_type_factory(child, parent=self),
                            attributes=base_type.attributes,
                            mixed=base_type.mixed,
                            block=base_type.block,
                            final=base_type.final,
                        )
                elif 'base' in elem.attrib:
                    self.parse_error(
                        "restriction with 'base' attribute and simpleType declaration"
                    )

                has_simple_type_child = True
            else:
                try:
                    facet_class = self._FACETS_BUILDERS[child.tag]
                except KeyError:
                    self.parse_error("unexpected tag %r in restriction:" % child.tag)
                    continue

                if child.tag not in facets:
                    facets[child.tag] = facet_class(child, self.schema, self, base_type)
                elif child.tag not in MULTIPLE_FACETS:
                    self.parse_error("multiple %r constraint facet" % local_name(child.tag))
                elif child.tag != XSD_ASSERTION:
                    facets[child.tag].append(child)
                else:
                    assertion = facet_class(child, self.schema, self, base_type)
                    try:
                        facets[child.tag].append(assertion)
                    except AttributeError:
                        facets[child.tag] = [facets[child.tag], assertion]

        if base_type is None:
            self.parse_error("missing base type in restriction:")
        elif base_type.final == '#all' or 'restriction' in base_type.final:
            self.parse_error("'final' value of the baseType %r forbids "
                             "derivation by restriction" % base_type)
        if base_type is self.any_atomic_type:
            self.parse_error("Cannot use xs:anyAtomicType as base type of a user-defined type")

        self.base_type = base_type
        self.facets = facets

    @property
    def variety(self) -> Optional[str]:
        return cast(Optional[str], getattr(self.base_type, 'variety', None))

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None:
            yield self
            for facet in self.facets.values():
                if isinstance(facet, list):
                    yield from facet  # XSD 1.1 assertions can be more than one
                elif isinstance(facet, XsdFacet):
                    yield facet  # only XSD facets, skip callables
        else:
            if isinstance(self, xsd_classes):
                yield self
            if issubclass(XsdFacet, xsd_classes):
                for facet in self.facets.values():
                    if isinstance(facet, list):
                        yield from facet
                    elif isinstance(facet, XsdFacet):
                        yield facet

        if self.base_type.parent is not None:
            yield from self.base_type.iter_components(xsd_classes)

    def iter_decode(self, obj: Union[str, bytes], validation: str = 'lax', **kwargs: Any) \
            -> IterDecodeType[DecodedValueType]:
        if isinstance(obj, (str, bytes)):
            obj = self.normalize(obj)

        base_type: Any
        if isinstance(self.base_type, XsdSimpleType):
            base_type = self.base_type
        elif self.base_type.has_simple_content():
            base_type = self.base_type.content
        elif self.base_type.mixed:
            yield obj
            return
        else:
            raise XMLSchemaValueError("wrong base type %r: a simpleType or a complexType with "
                                      "simple or mixed content required." % self.base_type)

        if self.patterns:
            if not isinstance(self.primitive_type, XsdUnion):
                try:
                    self.patterns(obj)
                except XMLSchemaValidationError as err:
                    yield err
            elif 'patterns' not in kwargs:
                kwargs['patterns'] = self.patterns

        for result in base_type.iter_decode(obj, validation, **kwargs):
            if isinstance(result, XMLSchemaValidationError):
                yield result
                if isinstance(result, XMLSchemaDecodeError):
                    yield str(obj) if validation == 'skip' else None
            else:
                if result is not None:
                    for validator in self.validators:
                        try:
                            validator(result)
                        except XMLSchemaValidationError as err:
                            yield err

                yield result
                return

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[EncodedValueType]:

        base_type: Any
        if self.is_list():
            if not hasattr(obj, '__iter__') or isinstance(obj, (str, bytes)):
                obj = [] if obj is None or obj == '' else [obj]
            base_type = self.base_type
        else:
            if isinstance(obj, (str, bytes)):
                obj = self.normalize(obj)

            if isinstance(self.base_type, XsdSimpleType):
                base_type = self.base_type
            elif self.base_type.has_simple_content():
                base_type = self.base_type.content
            elif self.base_type.mixed:
                yield str(obj)
                return
            else:
                raise XMLSchemaValueError("wrong base type %r: a simpleType or a complexType with "
                                          "simple or mixed content required." % self.base_type)

        result: Any
        for result in base_type.iter_encode(obj, validation):
            if isinstance(result, XMLSchemaValidationError):
                yield result
                if isinstance(result, XMLSchemaEncodeError):
                    yield str(obj) if validation == 'skip' else None
                    return
            else:
                if self.validators and obj is not None:
                    if isinstance(obj, (str, bytes)) and \
                            self.primitive_type.to_python is not str and \
                            isinstance(obj, self.primitive_type.instance_types):
                        try:
                            obj = self.primitive_type.to_python(obj)
                        except (ValueError, DecimalException, TypeError):
                            pass

                    for validator in self.validators:
                        try:
                            validator(obj)
                        except XMLSchemaValidationError as err:
                            yield err

                if self.patterns:
                    if not isinstance(self.primitive_type, XsdUnion):
                        try:
                            self.patterns(result)
                        except XMLSchemaValidationError as err:
                            yield err
                    elif 'patterns' not in kwargs:
                        kwargs['patterns'] = self.patterns

                yield result
                return

    def is_list(self) -> bool:
        return self.primitive_type.is_list()

    def is_union(self) -> bool:
        return self.primitive_type.is_union() class XsdUnion(XsdSimpleType):
    """
    Class for 'union' definitions. A union definition has a member_types
    attribute that refers to a 'simpleType' definition.

    ..  <union
          id = ID
          memberTypes = List of QName
          {any attributes with non-schema namespace ...}>
          Content: (annotation?, simpleType*)
        </union>
    """
    member_types: Any = ()
    _ADMITTED_TYPES: Any = XsdSimpleType
    _ADMITTED_TAGS = {XSD_UNION}

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: Optional[XsdComponent],
                 name: Optional[str] = None) -> None:
        super(XsdUnion, self).__init__(elem, schema, parent, name, facets=None)

    def __repr__(self) -> str:
        if self.name is None:
            return '%s(member_types=%r)' % (self.__class__.__name__, self.member_types)
        else:
            return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name)

    def __setattr__(self, name: str, value: Any) -> None:
        if name == 'elem' and value is not None and value.tag != XSD_UNION:
            if value.tag == XSD_SIMPLE_TYPE:
                for child in value:
                    if child.tag == XSD_UNION:
                        super(XsdUnion, self).__setattr__(name, child)
                        return
            raise XMLSchemaValueError("a %r definition required for %r." % (XSD_UNION, self))

        elif name == 'white_space':
            if not (value is None or value == 'collapse'):
                raise XMLSchemaValueError("Wrong value % for attribute 'white_space'." % value)
            value = 'collapse'
        super(XsdUnion, self).__setattr__(name, value)

    def _parse(self) -> None:
        mt: Any
        member_types = []

        for child in self.elem:
            if child.tag != XSD_ANNOTATION and not callable(child.tag):
                mt = self.schema.simple_type_factory(child, parent=self)
                if isinstance(mt, XMLSchemaParseError):
                    self.parse_error(mt)
                else:
                    member_types.append(mt)

        if 'memberTypes' in self.elem.attrib:
            for name in self.elem.attrib['memberTypes'].split():
                try:
                    type_qname = self.schema.resolve_qname(name)
                except (KeyError, ValueError, RuntimeError) as err:
                    self.parse_error(err)
                    continue

                try:
                    mt = self.maps.lookup_type(type_qname)
                except KeyError:
                    self.parse_error("unknown member type %r" % type_qname)
                    mt = self.any_atomic_type
                except XMLSchemaParseError as err:
                    self.parse_error(err)
                    mt = self.any_atomic_type

                if isinstance(mt, tuple):
                    self.parse_error(
                        "circular definition found on xs:union type {!r}".format(self.name)
                    )
                    continue
                elif not isinstance(mt, self._ADMITTED_TYPES):
                    self.parse_error("a {!r} required, not {!r}".format(self._ADMITTED_TYPES, mt))
                    continue
                elif mt.final == '#all' or 'union' in mt.final:
                    self.parse_error("'final' value of the memberTypes %r "
                                     "forbids derivation by union" % member_types)

                member_types.append(mt)

        if not member_types:
            self.parse_error("missing xs:union type declarations")
            self.member_types = [self.any_atomic_type]
        elif any(mt.name == XSD_ANY_ATOMIC_TYPE for mt in member_types):
            self.parse_error("Cannot use xs:anyAtomicType as base type of a user-defined type")
        else:
            self.member_types = member_types
            if all(not mt.allow_empty for mt in member_types):
                self.allow_empty = False

    @property
    def variety(self) -> Optional[str]:
        return 'union'

    @property
    def admitted_facets(self) -> Set[str]:
        return XSD_10_UNION_FACETS if self.xsd_version == '1.0' else XSD_11_UNION_FACETS

    def is_atomic(self) -> bool:
        return all(mt.is_atomic() for mt in self.member_types)

    def is_list(self) -> bool:
        return all(mt.is_list() for mt in self.member_types)

    def is_key(self) -> bool:
        return any(mt.is_key() for mt in self.member_types)

    def is_union(self) -> bool:
        return True

    def is_dynamic_consistent(self, other: Any) -> bool:
        return other.name in {XSD_ANY_TYPE, XSD_ANY_SIMPLE_TYPE} or \
            other.is_derived(self) or isinstance(other, self.__class__) and \
            any(mt1.is_derived(mt2) for mt1 in other.member_types for mt2 in self.member_types)

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self
        for mt in filter(lambda x: x.parent is not None, self.member_types):
            yield from mt.iter_components(xsd_classes)

    def iter_decode(self, obj: Any, validation: str = 'lax',
                    patterns: Optional[XsdPatternFacets] = None,
                    **kwargs: Any) -> IterDecodeType[DecodedValueType]:

        # Try decoding the whole text
        for member_type in self.member_types:
            for result in member_type.iter_decode(obj, validation='lax', **kwargs):
                if not isinstance(result, XMLSchemaValidationError):
                    if patterns:
                        obj = member_type.normalize(obj)
                        try:
                            patterns(obj)
                        except XMLSchemaValidationError as err:
                            yield err

                    yield result
                    return
                break

        if ' ' not in obj.strip():
            reason = "invalid value %r." % obj
            yield XMLSchemaDecodeError(self, obj, self.member_types, reason)

        items = []
        not_decodable = []
        for chunk in obj.split():
            for member_type in self.member_types:
                for result in member_type.iter_decode(chunk, validation='lax', **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        break
                    else:
                        items.append(result)
                else:
                    break
            else:
                if validation != 'skip':
                    not_decodable.append(chunk)
                else:
                    items.append(str(chunk))

        if not_decodable:
            reason = "no type suitable for decoding the values %r." % not_decodable
            yield XMLSchemaDecodeError(self, obj, self.member_types, reason)

        yield items if len(items) > 1 else items[0] if items else None

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[EncodedValueType]:

        for member_type in self.member_types:
            for result in member_type.iter_encode(obj, validation='lax', **kwargs):
                if result is not None and not isinstance(result, XMLSchemaValidationError):
                    yield result
                    return
                elif validation == 'strict':
                    # In 'strict' mode avoid lax encoding by similar types
                    # (eg. float encoded by int)
                    break

        if hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes)):
            for member_type in self.member_types:
                results = []
                for item in obj:
                    for result in member_type.iter_encode(item, validation='lax', **kwargs):
                        if result is not None and not isinstance(result, XMLSchemaValidationError):
                            results.append(result)
                            break
                        elif validation == 'strict':
                            break

                if len(results) == len(obj):
                    yield results
                    break

        if validation != 'skip':
            reason = "no type suitable for encoding the object."
            yield XMLSchemaEncodeError(self, obj, self.member_types, reason)
            yield None
        else:
            yield str(obj) class XsdComplexType(XsdType, ValidationMixin[Union[ElementType, str, bytes], Any]):
    """
    Class for XSD 1.0 *complexType* definitions.

    :var attributes: the attribute group related with the complexType.
    :var content: the content of the complexType can be a model group or a simple type.
    :var mixed: if `True` the complex type has mixed content.

    ..  <complexType
          abstract = boolean : false
          block = (#all | List of (extension | restriction))
          final = (#all | List of (extension | restriction))
          id = ID
          mixed = boolean : false
          name = NCName
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (simpleContent | complexContent |
          ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
        </complexType>
    """
    attributes: XsdAttributeGroup
    copy: Callable[['XsdComplexType'], 'XsdComplexType']
    content: Union[XsdGroup, XsdSimpleType] = None  # type: ignore[assignment]

    abstract: bool = False
    mixed: bool = False
    assertions: Union[Tuple[()], List[XsdAssert]] = ()
    open_content: Optional[XsdOpenContent] = None
    _block: Optional[str] = None

    _ADMITTED_TAGS = {XSD_COMPLEX_TYPE, XSD_RESTRICTION}
    _CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ANY_ATTRIBUTE}

    @staticmethod
    def normalize(text: Union[str, bytes]) -> str:
        return text.decode('utf-8') if isinstance(text, bytes) else text

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: Optional[XsdComponent] = None,
                 name: Optional[str] = None,
                 **kwargs: Any) -> None:

        if kwargs:
            if 'content' in kwargs:
                self.content = kwargs['content']
            if 'attributes' in kwargs:
                self.attributes = kwargs['attributes']
            if 'mixed' in kwargs:
                self.mixed = kwargs['mixed']
            if 'block' in kwargs:
                self._block = kwargs['block']
            if 'final' in kwargs:
                self._final = kwargs['final']
        super(XsdComplexType, self).__init__(elem, schema, parent, name)
        assert self.content is not None

    def __repr__(self) -> str:
        if self.name is not None:
            return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name)
        elif not hasattr(self, 'content') or not hasattr(self, 'attributes'):
            return '%s(id=%r)' % (self.__class__.__name__, id(self))
        else:
            return '%s(content=%r, attributes=%r)' % (
                self.__class__.__name__, self.content_type_label,
                [a if a.name is None else a.prefixed_name for a in self.attributes.values()]
            )

    def _parse(self) -> None:
        if self.elem.tag == XSD_RESTRICTION:
            return  # a local restriction is already parsed by the caller

        if 'abstract' in self.elem.attrib:
            if self.elem.attrib['abstract'].strip() in {'true', '1'}:
                self.abstract = True

        if 'block' in self.elem.attrib:
            try:
                self._block = get_xsd_derivation_attribute(self.elem, 'block', XSD_TYPE_DERIVATIONS)
            except ValueError as err:
                self.parse_error(err)

        if 'final' in self.elem.attrib:
            try:
                self._final = get_xsd_derivation_attribute(self.elem, 'final', XSD_TYPE_DERIVATIONS)
            except ValueError as err:
                self.parse_error(err)

        if 'mixed' in self.elem.attrib:
            if self.elem.attrib['mixed'].strip() in {'true', '1'}:
                self.mixed = True

        try:
            self.name = get_qname(self.target_namespace, self.elem.attrib['name'])
        except KeyError:
            self.name = None
            if self.parent is None:
                self.parse_error("missing attribute 'name' in a global complexType")
                self.name = 'nameless_%s' % str(id(self))
        else:
            if self.parent is not None:
                self.parse_error("attribute 'name' not allowed for a local complexType")
                self.name = None

        content_elem = self._parse_child_component(self.elem, strict=False)
        if content_elem is None or content_elem.tag in self._CONTENT_TAIL_TAGS:
            self.content = self.schema.create_empty_content_group(self)
            self._parse_content_tail(self.elem)
            default_open_content = self.default_open_content
            if default_open_content is not None and \
                    (self.mixed or self.content or default_open_content.applies_to_empty):
                self.open_content = default_open_content

        elif content_elem.tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
            self.content = self.schema.xsd_group_class(content_elem, self.schema, self)
            default_open_content = self.default_open_content
            if default_open_content is not None and \
                    (self.mixed or self.content or default_open_content.applies_to_empty):
                self.open_content = default_open_content
            self._parse_content_tail(self.elem)

        elif content_elem.tag == XSD_SIMPLE_CONTENT:
            if 'mixed' in content_elem.attrib:
                self.parse_error("'mixed' attribute not allowed with simpleContent", content_elem)

            derivation_elem = self._parse_derivation_elem(content_elem)
            if derivation_elem is None:
                return

            self.base_type = base_type = self._parse_base_type(derivation_elem)
            if derivation_elem.tag == XSD_RESTRICTION:
                self._parse_simple_content_restriction(derivation_elem, base_type)
            else:
                self._parse_simple_content_extension(derivation_elem, base_type)

            if content_elem is not self.elem[-1]:
                k = 2 if content_elem is not self.elem[0] else 1
                self.parse_error(
                    "unexpected tag %r after simpleContent declaration:" % self.elem[k].tag
                )

        elif content_elem.tag == XSD_COMPLEX_CONTENT:
            #
            # complexType with complexContent restriction/extension
            if 'mixed' in content_elem.attrib:
                mixed = content_elem.attrib['mixed'] in ('true', '1')
                if mixed is not self.mixed:
                    self.mixed = mixed
                    if 'mixed' in self.elem.attrib and self.xsd_version == '1.1':
                        self.parse_error("value of 'mixed' attribute in complexType "
                                         "and complexContent must be same")

            derivation_elem = self._parse_derivation_elem(content_elem)
            if derivation_elem is None:
                return

            self.base_type = self._parse_base_type(derivation_elem, complex_content=True)
            if self.base_type is self and self.redefine is not None:
                self.base_type = self.redefine
                self.open_content = None

            if derivation_elem.tag == XSD_RESTRICTION:
                self._parse_complex_content_restriction(derivation_elem, self.base_type)
            else:
                self._parse_complex_content_extension(derivation_elem, self.base_type)

            if content_elem is not self.elem[-1]:
                k = 2 if content_elem is not self.elem[0] else 1
                self.parse_error(
                    "unexpected tag %r after complexContent declaration:" % self.elem[k].tag
                )

        elif content_elem.tag == XSD_OPEN_CONTENT and self.xsd_version > '1.0':
            self.open_content = XsdOpenContent(content_elem, self.schema, self)

            if content_elem is self.elem[-1]:
                self.content = self.schema.create_empty_content_group(self)
            else:
                for index, child in enumerate(self.elem):
                    if content_elem is not child:
                        continue
                    elif self.elem[index + 1].tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
                        self.content = self.schema.xsd_group_class(
                            self.elem[index + 1], self.schema, self
                        )
                    else:
                        self.content = self.schema.create_empty_content_group(self)
                    break
            self._parse_content_tail(self.elem)

        else:
            if self.schema.validation == 'skip':
                # Also generated by meta-schema validation for 'lax' and 'strict' modes
                self.parse_error(
                    "unexpected tag %r for complexType content:" % content_elem.tag
                )
            self.content = self.schema.create_any_content_group(self)
            self.attributes = self.schema.create_any_attribute_group(self)

        if self.redefine is None:
            if self.base_type is not None and self.base_type.name == self.name:
                self.parse_error("wrong definition with self-reference")
        elif self.base_type is None or self.base_type.name != self.name:
            self.parse_error("wrong redefinition without self-reference")

    def _parse_content_tail(self, elem: ElementType, **kwargs: Any) -> None:
        self.attributes = self.schema.xsd_attribute_group_class(
            elem, self.schema, self, **kwargs
        )

    def _parse_derivation_elem(self, elem: ElementType) -> Optional[ElementType]:
        derivation_elem = self._parse_child_component(elem)
        if derivation_elem is None or derivation_elem.tag not in {XSD_RESTRICTION, XSD_EXTENSION}:
            self.parse_error("restriction or extension tag expected", derivation_elem)
            self.content = self.schema.create_any_content_group(self)
            self.attributes = self.schema.create_any_attribute_group(self)
            return None

        if self.derivation is not None and self.redefine is None:
            raise XMLSchemaValueError("{!r} is expected to have a redefined/"
                                      "overridden component".format(self))
        self.derivation = local_name(derivation_elem.tag)

        if self.base_type is not None and self.derivation in self.base_type.final:
            self.parse_error(f"{self.derivation!r} derivation not allowed for {self!r}")
        return derivation_elem

    def _parse_base_type(self, elem: ElementType, complex_content: bool = False) \
            -> Union[XsdSimpleType, 'XsdComplexType']:
        try:
            base_qname = self.schema.resolve_qname(elem.attrib['base'])
        except (KeyError, ValueError, RuntimeError) as err:
            if 'base' not in elem.attrib:
                self.parse_error("'base' attribute required", elem)
            else:
                self.parse_error(err, elem)
            return self.any_type

        try:
            base_type = self.maps.lookup_type(base_qname)
        except KeyError:
            self.parse_error("missing base type %r" % base_qname, elem)
            if complex_content:
                return self.any_type
            else:
                return self.any_simple_type
        else:
            if isinstance(base_type, tuple):
                self.parse_error("circularity definition found between %r "
                                 "and %r" % (self, base_qname), elem)
                return self.any_type
            elif complex_content and base_type.is_simple():
                self.parse_error("a complexType ancestor required: %r" % base_type, elem)
                return self.any_type

            if base_type.final and elem.tag.rsplit('}', 1)[-1] in base_type.final:
                msg = "derivation by %r blocked by attribute 'final' in base type"
                self.parse_error(msg % elem.tag.rsplit('}', 1)[-1])

            return base_type

    def _parse_simple_content_restriction(self, elem: ElementType, base_type: Any) -> None:
        # simpleContent restriction: the base type must be a complexType with a simple
        # content or a complex content with a mixed and emptiable content.
        if base_type.is_simple():
            self.parse_error("a complexType ancestor required: %r" % base_type, elem)
            self.content = self.schema.create_any_content_group(self)
            self._parse_content_tail(elem)
        else:
            if base_type.is_empty():
                self.content = self.schema.xsd_atomic_restriction_class(elem, self.schema, self)
                if not self.is_empty():
                    self.parse_error("a not empty simpleContent cannot restrict "
                                     "an empty content type", elem)
                    self.content = self.schema.create_any_content_group(self)

            elif base_type.has_simple_content():
                self.content = self.schema.xsd_atomic_restriction_class(elem, self.schema, self)
                if not self.content.is_derived(base_type.content, 'restriction'):
                    self.parse_error("content type is not a restriction of base content", elem)

            elif base_type.mixed and base_type.is_emptiable():
                self.content = self.schema.xsd_atomic_restriction_class(elem, self.schema, self)
            else:
                self.parse_error("with simpleContent cannot restrict an "
                                 "element-only content type", elem)
                self.content = self.schema.create_any_content_group(self)

            self._parse_content_tail(elem, derivation='restriction',
                                     base_attributes=base_type.attributes)

    def _parse_simple_content_extension(self, elem: ElementType, base_type: Any) -> None:
        # simpleContent extension: the base type must be a simpleType or a complexType
        # with simple content.
        child = self._parse_child_component(elem, strict=False)
        if child is not None and child.tag not in self._CONTENT_TAIL_TAGS:
            self.parse_error('unexpected tag %r' % child.tag, child)

        if base_type.is_simple():
            self.content = base_type
            self._parse_content_tail(elem)
        else:
            if base_type.has_simple_content():
                self.content = base_type.content
            else:
                self.parse_error("base type %r has not simple content." % base_type, elem)
                self.content = self.schema.create_any_content_group(self)

            self._parse_content_tail(elem, derivation='extension',
                                     base_attributes=base_type.attributes)

    def _parse_complex_content_restriction(self, elem: ElementType, base_type: Any) -> None:
        if 'restriction' in base_type.final:
            self.parse_error("the base type is not derivable by restriction")
        if base_type.is_simple() or base_type.has_simple_content():
            self.parse_error("base %r is simple or has a simple content." % base_type, elem)
            base_type = self.any_type

        # complexContent restriction: the base type must be a complexType with a complex content.
        for child in elem:
            if child.tag == XSD_OPEN_CONTENT and self.xsd_version > '1.0':
                self.open_content = XsdOpenContent(child, self.schema, self)
                continue
            elif child.tag in XSD_MODEL_GROUP_TAGS:
                content = self.schema.xsd_group_class(child, self.schema, self)
                if not base_type.content.admits_restriction(content.model):
                    self.parse_error(
                        "restriction of an xs:{} with more than one particle with xs:{} is "
                        "forbidden".format(base_type.content.model, content.model)
                    )
                break
        else:
            content = self.schema.create_empty_content_group(
                self, base_type.content.model
            )

        content.restriction = base_type.content

        if base_type.is_element_only() and content.mixed:
            self.parse_error(
                "derived a mixed content from a base type that has element-only content.", elem
            )
        elif base_type.is_empty() and not content.is_empty():
            self.parse_error(
                "derived an empty content from base type that has not empty content.", elem
            )

        if self.open_content is None:
            default_open_content = self.default_open_content
            if default_open_content is not None and \
                    (self.mixed or content or default_open_content.applies_to_empty):
                self.open_content = default_open_content

        if self.open_content and content and \
                not self.open_content.is_restriction(base_type.open_content):
            msg = "{!r} is not a restriction of the base type {!r}"
            self.parse_error(msg.format(self.open_content, base_type.open_content))

        self.content = content
        self._parse_content_tail(elem, derivation='restriction',
                                 base_attributes=base_type.attributes)

    def _parse_complex_content_extension(self, elem: ElementType, base_type: Any) -> None:
        if 'extension' in base_type.final:
            self.parse_error("the base type is not derivable by extension")

        group_elem: Optional[ElementType]
        for group_elem in elem:
            if group_elem.tag != XSD_ANNOTATION and not callable(group_elem.tag):
                break
        else:
            group_elem = None

        if base_type.is_empty():
            if not base_type.mixed:
                # Empty element-only model extension: don't create a nested group.
                if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
                    self.content = self.schema.xsd_group_class(
                        group_elem, self.schema, self
                    )
                elif base_type.is_simple() or base_type.has_simple_content():
                    self.content = self.schema.create_empty_content_group(self)
                else:
                    self.content = self.schema.create_empty_content_group(
                        parent=self, elem=base_type.content.elem
                    )
            elif base_type.mixed:
                # Empty mixed model extension
                self.content = self.schema.create_empty_content_group(self)
                self.content.append(self.schema.create_empty_content_group(self.content))

                if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
                    group = self.schema.xsd_group_class(
                        group_elem, self.schema, self.content
                    )
                    if not self.mixed:
                        self.parse_error("base has a different content type (mixed=%r) and the "
                                         "extension group is not empty." % base_type.mixed, elem)
                else:
                    group = self.schema.create_empty_content_group(self)

                self.content.append(group)
                self.content.elem.append(base_type.content.elem)
                self.content.elem.append(group.elem)

        elif group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
            # Derivation from a simple content is forbidden if base type is not empty.
            if base_type.is_simple() or base_type.has_simple_content():
                self.parse_error("base %r is simple or has a simple content." % base_type, elem)
                base_type = self.any_type

            group = self.schema.xsd_group_class(group_elem, self.schema, self)

            if group.model == 'all':
                self.parse_error("cannot extend a complex content with xs:all")
            if base_type.content.model == 'all' and group.model == 'sequence':
                self.parse_error("xs:sequence cannot extend xs:all")

            content = self.schema.create_empty_content_group(self)
            content.append(base_type.content)
            content.append(group)
            content.elem.append(base_type.content.elem)
            content.elem.append(group.elem)

            if base_type.content.model == 'all' and base_type.content and group:
                self.parse_error(
                    "XSD 1.0 does not allow extension of a not empty 'all' model group"
                )
            if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
                self.parse_error("base has a different content type (mixed=%r) and the "
                                 "extension group is not empty" % base_type.mixed, elem)
            self.content = content

        elif base_type.is_simple():
            self.content = base_type
        elif base_type.has_simple_content():
            self.content = base_type.content
        else:
            self.content = self.schema.create_empty_content_group(self)
            self.content.append(base_type.content)
            self.content.elem.append(base_type.content.elem)
            if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
                self.parse_error(
                    "extended type has a mixed content but the base is element-only", elem
                )

        self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)

    @property
    def default_open_content(self) -> Optional[XsdDefaultOpenContent]:
        return None

    @property
    def block(self) -> str:
        return self.schema.block_default if self._block is None else self._block

    @property
    def built(self) -> bool:
        return self.content.parent is not None or self.content.built

    @property
    def validation_attempted(self) -> str:
        return 'full' if self.built else self.content.validation_attempted

    @property
    def simple_type(self) -> Optional[XsdSimpleType]:
        return self.content if isinstance(self.content, XsdSimpleType) else None

    @property
    def model_group(self) -> Optional[XsdGroup]:
        return self.content if isinstance(self.content, XsdGroup) else None

    @property
    def content_type(self) -> Union[XsdGroup, XsdSimpleType]:
        """Property that returns the attribute *content*, for backward compatibility."""
        import warnings
        warnings.warn("'content_type' attribute has been replaced by 'content' "
                      "and will be removed in version 2.0", DeprecationWarning, stacklevel=2)
        return self.content

    @property
    def content_type_label(self) -> str:
        if self.is_empty():
            return 'empty'
        elif isinstance(self.content, XsdSimpleType):
            return 'simple'
        elif self.mixed:
            return 'mixed'
        else:
            return 'element-only'

    @property
    def sequence_type(self) -> str:
        if self.is_empty():
            return 'empty-sequence()'
        elif not self.has_simple_content():
            st = get_prefixed_qname(XSD_UNTYPED_ATOMIC, self.namespaces)
        else:
            try:
                st = self.content.primitive_type.prefixed_name  # type: ignore[union-attr]
            except AttributeError:
                st = get_prefixed_qname(XSD_UNTYPED_ATOMIC, self.namespaces)
            else:
                if st is None:
                    st = 'item()'

        return f"{st}{'*' if self.is_emptiable() else '+'}"

    @staticmethod
    def is_simple() -> bool:
        return False

    @staticmethod
    def is_complex() -> bool:
        return True

    def is_empty(self) -> bool:
        if self.open_content and self.open_content.mode != 'none':
            return False
        return self.content.is_empty()

    def is_emptiable(self) -> bool:
        return self.content.is_emptiable()

    def has_simple_content(self) -> bool:
        if not isinstance(self.content, XsdGroup):
            return not self.content.is_empty()
        elif self.content or self.content.mixed or self.base_type is None:
            return False
        else:
            return self.base_type.is_simple() or self.base_type.has_simple_content()

    def has_complex_content(self) -> bool:
        if not isinstance(self.content, XsdGroup):
            return False
        elif self.open_content and self.open_content.mode != 'none':
            return True
        return not self.content.is_empty()

    def has_mixed_content(self) -> bool:
        if not isinstance(self.content, XsdGroup):
            return False
        elif self.content.is_empty():
            return False
        else:
            return self.content.mixed

    def is_element_only(self) -> bool:
        if not isinstance(self.content, XsdGroup):
            return False
        elif self.content.is_empty():
            return False
        else:
            return not self.content.mixed

    def is_list(self) -> bool:
        return isinstance(self.content, XsdSimpleType) and self.content.is_list()

    def validate(self, obj: Union[ElementType, str, bytes],
                 use_defaults: bool = True,
                 namespaces: Optional[NamespacesType] = None,
                 max_depth: Optional[int] = None,
                 extra_validator: Optional[ExtraValidatorType] = None) -> None:
        kwargs: Any = {
            'use_defaults': use_defaults,
            'namespaces': namespaces,
            'max_depth': max_depth,
            'extra_validator': extra_validator
        }
        if not isinstance(obj, (str, bytes)):
            super(XsdComplexType, self).validate(obj, **kwargs)
        elif isinstance(self.content, XsdSimpleType):
            self.content.validate(obj, **kwargs)
        elif not self.mixed and self.base_type is not None:
            self.base_type.validate(obj, **kwargs)

    def is_valid(self, obj: Union[ElementType, str, bytes],
                 use_defaults: bool = True,
                 namespaces: Optional[NamespacesType] = None,
                 max_depth: Optional[int] = None,
                 extra_validator: Optional[ExtraValidatorType] = None) -> bool:
        kwargs: Any = {
            'use_defaults': use_defaults,
            'namespaces': namespaces,
            'max_depth': max_depth,
            'extra_validator': extra_validator
        }
        if not isinstance(obj, (str, bytes)):
            return super(XsdComplexType, self).is_valid(obj, **kwargs)
        elif isinstance(self.content, XsdSimpleType):
            return self.content.is_valid(obj, **kwargs)
        else:
            return self.mixed or self.base_type is not None and \
                self.base_type.is_valid(obj, **kwargs)

    def is_derived(self, other: Union[BaseXsdType, Tuple[ElementType, SchemaType]],
                   derivation: Optional[str] = None) -> bool:
        if derivation and derivation == self.derivation:
            derivation = None  # derivation mode checked

        if self is other:
            return derivation is None
        elif isinstance(other, tuple):
            other[1].parse_error(f"global type {other[0].tag!r} is not built")
            return False
        elif other.name == XSD_ANY_TYPE:
            return True
        elif self.base_type is other:
            return derivation is None  # or self.base_type.derivation == derivation
        elif isinstance(other, XsdUnion):
            return any(self.is_derived(m, derivation) for m in other.member_types)
        elif self.base_type is None:
            if not self.has_simple_content():
                return False
            return isinstance(self.content, XsdSimpleType) and \
                self.content.is_derived(other, derivation)
        elif self.has_simple_content():
            return isinstance(self.content, XsdSimpleType) and \
                self.content.is_derived(other, derivation) or \
                self.base_type.is_derived(other, derivation)
        else:
            return self.base_type.is_derived(other, derivation)

    def iter_components(self, xsd_classes: ComponentClassType = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self
        if self.attributes and self.attributes.parent is not None:
            yield from self.attributes.iter_components(xsd_classes)
        if self.content.parent is not None:
            yield from self.content.iter_components(xsd_classes)
        if self.base_type is not None and self.base_type.parent is not None:
            yield from self.base_type.iter_components(xsd_classes)

        for obj in filter(lambda x: x.base_type is self, self.assertions):
            if xsd_classes is None or isinstance(obj, xsd_classes):
                yield obj

    def get_facet(self, tag: str) -> Optional[FacetsValueType]:
        if isinstance(self.content, XsdSimpleType):
            return self.content.get_facet(tag)
        return None

    def admit_simple_restriction(self) -> bool:
        if 'restriction' in self.final:
            return False
        else:
            return self.has_simple_content() or self.mixed and self.is_emptiable()

    def has_restriction(self) -> bool:
        return self.derivation == 'restriction'

    def has_extension(self) -> bool:
        return self.derivation == 'extension'

    def text_decode(self, text: str) -> Optional[AtomicValueType]:
        if isinstance(self.content, XsdSimpleType):
            return cast(Optional[AtomicValueType], self.content.decode(text, 'skip'))
        else:
            return text

    def decode(self, obj: Union[ElementType, str, bytes], *args: Any, **kwargs: Any) \
            -> DecodeType[Any]:
        if not isinstance(obj, (str, bytes)):
            return super(XsdComplexType, self).decode(obj, *args, **kwargs)
        elif isinstance(self.content, XsdSimpleType):
            return self.content.decode(obj, *args, **kwargs)
        else:
            raise XMLSchemaDecodeError(
                self, obj, str, "cannot decode %r data with %r" % (obj, self)
            )

    def iter_decode(self, obj: Union[ElementType, str, bytes],
                    validation: str = 'lax', **kwargs: Any) -> IterDecodeType[Any]:
        """
        Decodes an Element instance using a dummy XSD element. Typically used
        for decoding with xs:anyType when an XSD element is not available.
        Also decodes strings if the type has a simple content.

        :param obj: the XML data that has to be decoded.
        :param validation: the validation mode. Can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the decoding process.
        :return: yields a decoded object, eventually preceded by a sequence of \
        validation or decoding errors.
        """
        if not isinstance(obj, (str, bytes)):
            xsd_element = self.schema.create_element(obj.tag, parent=self, form='unqualified')
            xsd_element.type = self
            yield from xsd_element.iter_decode(obj, validation, **kwargs)
        elif isinstance(self.content, XsdSimpleType):
            yield from self.content.iter_decode(obj, validation, **kwargs)
        else:
            raise XMLSchemaDecodeError(
                self, obj, str, "cannot decode %r data with %r" % (obj, self)
            )

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[ElementType]:
        """
        Encode XML data. A dummy element is created for the type and it's used for
        encode data. Typically used for encoding with xs:anyType when an XSD element
        is not available.

        :param obj: decoded XML data.
        :param validation: the validation mode: can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the encoding process.
        :return: yields an Element, eventually preceded by a sequence of \
        validation or encoding errors.
        """
        try:
            name, value = obj
        except ValueError:
            name = obj.name
            value = obj

        xsd_element = self.schema.create_element(name, parent=self, form='unqualified')
        xsd_element.type = self

        if isinstance(value, MutableSequence) and not isinstance(value, dataobjects.DataElement):
            try:
                results = [x for item in value for x in xsd_element.iter_encode(
                    item, validation, **kwargs
                )]
            except XMLSchemaValueError:
                pass
            else:
                yield from results
                return

        yield from xsd_element.iter_encode(value, validation, **kwargs) class XsdGroup(XsdComponent, MutableSequence[ModelParticleType],
               ParticleMixin, ValidationMixin[ElementType, GroupDecodeType]):
    """
    Class for XSD 1.0 *model group* definitions.

    ..  <group
          id = ID
          maxOccurs = (nonNegativeInteger | unbounded) : 1
          minOccurs = nonNegativeInteger : 1
          name = NCName
          ref = QName
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (all | choice | sequence)?)
        </group>

    ..  <all
          id = ID
          maxOccurs = 1 : 1
          minOccurs = (0 | 1) : 1
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, element*)
        </all>

    ..  <choice
          id = ID
          maxOccurs = (nonNegativeInteger | unbounded)  : 1
          minOccurs = nonNegativeInteger : 1
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (element | group | choice | sequence | any)*)
        </choice>

    ..  <sequence
          id = ID
          maxOccurs = (nonNegativeInteger | unbounded)  : 1
          minOccurs = nonNegativeInteger : 1
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, (element | group | choice | sequence | any)*)
        </sequence>
    """
    parent: Optional[Union['XsdComplexType', 'XsdGroup']]
    model: str
    mixed: bool = False
    ref: Optional['XsdGroup']
    restriction: Optional['XsdGroup'] = None

    # For XSD 1.1 openContent processing
    interleave: Optional[Xsd11AnyElement] = None  # if openContent with mode='interleave'
    suffix: Optional[Xsd11AnyElement] = None  # if openContent with mode='suffix'/'interleave'

    _ADMITTED_TAGS = {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}

    def __init__(self, elem: ElementType,
                 schema: SchemaType,
                 parent: Optional[Union['XsdComplexType', 'XsdGroup']] = None) -> None:

        self._group: List[ModelParticleType] = []
        if parent is not None and parent.mixed:
            self.mixed = parent.mixed
        super(XsdGroup, self).__init__(elem, schema, parent)

    def __repr__(self) -> str:
        if self.name is None:
            return '%s(model=%r, occurs=%r)' % (
                self.__class__.__name__, self.model, list(self.occurs)
            )
        elif self.ref is None:
            return '%s(name=%r, model=%r, occurs=%r)' % (
                self.__class__.__name__, self.prefixed_name, self.model, list(self.occurs)
            )
        else:
            return '%s(ref=%r, model=%r, occurs=%r)' % (
                self.__class__.__name__, self.prefixed_name, self.model, list(self.occurs)
            )

    @overload
    def __getitem__(self, i: int) -> ModelParticleType: ...

    @overload
    def __getitem__(self, s: slice) -> MutableSequence[ModelParticleType]: ...

    def __getitem__(self, i: Union[int, slice]) \
            -> Union[ModelParticleType, MutableSequence[ModelParticleType]]:
        return self._group[i]

    def __setitem__(self, i: Union[int, slice], o: Any) -> None:
        self._group[i] = o

    def __delitem__(self, i: Union[int, slice]) -> None:
        del self._group[i]

    def __len__(self) -> int:
        return len(self._group)

    def insert(self, i: int, item: ModelParticleType) -> None:
        self._group.insert(i, item)

    def clear(self) -> None:
        del self._group[:]

    def is_emptiable(self) -> bool:
        if self.model == 'choice':
            return self.min_occurs == 0 or not self or any(item.is_emptiable() for item in self)
        else:
            return self.min_occurs == 0 or not self or all(item.is_emptiable() for item in self)

    def is_single(self) -> bool:
        if self.max_occurs != 1 or not self:
            return False
        elif len(self) > 1 or not isinstance(self[0], XsdGroup):
            return True
        else:
            return self[0].is_single()

    def is_pointless(self, parent: 'XsdGroup') -> bool:
        """
        Returns `True` if the group may be eliminated without affecting the model,
        `False` otherwise. A group is pointless if one of those conditions is verified:

         - the group is empty
         - minOccurs == maxOccurs == 1 and the group has one child
         - minOccurs == maxOccurs == 1 and the group and its parent have a sequence model
         - minOccurs == maxOccurs == 1 and the group and its parent have a choice model

        Ref: https://www.w3.org/TR/2004/REC-xmlschema-1-20041028/#coss-particle

        :param parent: effective parent of the model group.
        """
        if not self:
            return True
        elif self.min_occurs != 1 or self.max_occurs != 1:
            return False
        elif len(self) == 1:
            return True
        elif self.model == 'sequence' and parent.model != 'sequence':
            return False
        elif self.model == 'choice' and parent.model != 'choice':
            return False
        else:
            return True

    @property
    def effective_min_occurs(self) -> int:
        if not self.min_occurs or not self:
            return 0
        elif self.model == 'choice':
            if any(not e.effective_min_occurs for e in self.iter_model()):
                return 0
        else:
            if all(not e.effective_min_occurs for e in self.iter_model()):
                return 0
        return self.min_occurs

    @property
    def effective_max_occurs(self) -> Optional[int]:
        if self.max_occurs == 0 or not self:
            return 0

        effective_items: List[Any]
        value: int

        effective_items = [e for e in self.iter_model() if e.effective_max_occurs != 0]
        if not effective_items:
            return 0
        elif self.max_occurs is None:
            return None
        elif self.model == 'choice':
            try:
                value = max(e.effective_max_occurs for e in effective_items)
            except TypeError:
                return None
            else:
                return self.max_occurs * value

        not_emptiable_items = [e for e in effective_items if e.effective_min_occurs]
        if not not_emptiable_items:
            try:
                value = max(e.effective_max_occurs for e in effective_items)
            except TypeError:
                return None
            else:
                return self.max_occurs * value

        elif len(not_emptiable_items) > 1:
            return self.max_occurs

        value = not_emptiable_items[0].effective_max_occurs
        return None if value is None else self.max_occurs * value

    def has_occurs_restriction(
            self, other: Union[ModelParticleType, ParticleMixin, 'OccursCalculator']) -> bool:

        if not self:
            return True
        elif isinstance(other, XsdGroup):
            return super(XsdGroup, self).has_occurs_restriction(other)

        # Group particle compared to element particle
        if self.max_occurs is None or any(e.max_occurs is None for e in self):
            if other.max_occurs is not None:
                return False
            elif self.model == 'choice':
                return self.min_occurs * min(e.min_occurs for e in self) >= other.min_occurs
            else:
                return self.min_occurs * sum(e.min_occurs for e in self) >= other.min_occurs

        elif self.model == 'choice':
            if self.min_occurs * min(e.min_occurs for e in self) < other.min_occurs:
                return False
            elif other.max_occurs is None:
                return True
            else:
                value: int
                try:
                    value = max(e.max_occurs for e in self)  # type: ignore[type-var, assignment]
                except TypeError:
                    return False
                else:
                    return self.max_occurs * value <= other.max_occurs

        else:
            if self.min_occurs * sum(e.min_occurs for e in self) < other.min_occurs:
                return False
            elif other.max_occurs is None:
                return True
            else:
                try:
                    value = sum(e.max_occurs for e in self)  # type: ignore[misc]
                except TypeError:
                    return False
                else:
                    return self.max_occurs * value <= other.max_occurs

    def iter_model(self) -> Iterator[ModelParticleType]:
        """
        A generator function iterating elements and groups of a model group.
        Skips pointless groups, iterating deeper through them.
        Raises `XMLSchemaModelDepthError` if the *depth* of the model is over
        `limits.MAX_MODEL_DEPTH` value.
        """
        iterators: List[Iterator[ModelParticleType]] = []
        particles = iter(self)

        while True:
            try:
                item = next(particles)
            except StopIteration:
                try:
                    particles = iterators.pop()
                except IndexError:
                    return
            else:
                if isinstance(item, XsdGroup) and item.is_pointless(parent=self):
                    iterators.append(particles)
                    particles = iter(item)
                    if len(iterators) > limits.MAX_MODEL_DEPTH:
                        raise XMLSchemaModelDepthError(self)
                else:
                    yield item

    def iter_elements(self) -> Iterator[SchemaElementType]:
        """
        A generator function iterating model's elements. Raises `XMLSchemaModelDepthError`
        if the overall depth of the model groups is over `limits.MAX_MODEL_DEPTH`.
        """
        if self.max_occurs == 0:
            return

        iterators: List[Iterator[ModelParticleType]] = []
        particles = iter(self)

        while True:
            try:
                item = next(particles)
            except StopIteration:
                try:
                    particles = iterators.pop()
                except IndexError:
                    return
            else:
                if isinstance(item, XsdGroup):
                    iterators.append(particles)
                    particles = iter(item)
                    if len(iterators) > limits.MAX_MODEL_DEPTH:
                        raise XMLSchemaModelDepthError(self)
                else:
                    yield item

    def get_subgroups(self, item: ModelParticleType) -> List['XsdGroup']:
        """
        Returns a list of the groups that represent the path to the enclosed particle.
        Raises an `XMLSchemaModelError` if *item* is not a particle of the model group.
        """
        subgroups: List[Tuple[XsdGroup, Iterator[ModelParticleType]]] = []
        group, children = self, iter(self)

        while True:
            try:
                child = next(children)
            except StopIteration:
                try:
                    group, children = subgroups.pop()
                except IndexError:
                    msg = '{!r} is not a particle of the model group'
                    raise XMLSchemaModelError(self, msg.format(item)) from None
                else:
                    continue

            if child is item:
                _subgroups = [x[0] for x in subgroups]
                _subgroups.append(group)
                return _subgroups
            elif isinstance(child, XsdGroup):
                if len(subgroups) > limits.MAX_MODEL_DEPTH:
                    raise XMLSchemaModelDepthError(self)
                subgroups.append((group, children))
                group, children = child, iter(child)

    def overall_min_occurs(self, item: ModelParticleType) -> int:
        """Returns the overall min occurs of a particle in the model."""
        min_occurs = item.min_occurs

        for group in self.get_subgroups(item):
            if group.model == 'choice' and len(group) > 1:
                return 0
            min_occurs *= group.min_occurs

        return min_occurs

    def overall_max_occurs(self, item: ModelParticleType) -> Optional[int]:
        """Returns the overall max occurs of a particle in the model."""
        max_occurs = item.max_occurs

        for group in self.get_subgroups(item):
            if max_occurs == 0:
                return 0
            elif max_occurs is None:
                continue
            elif group.max_occurs is None:
                max_occurs = None
            else:
                max_occurs *= group.max_occurs

        return max_occurs

    def copy(self) -> 'XsdGroup':
        group: XsdGroup = object.__new__(self.__class__)
        group.__dict__.update(self.__dict__)
        group.errors = self.errors[:]
        group._group = self._group[:]
        return group

    __copy__ = copy

    def _parse(self) -> None:
        self.clear()
        self._parse_particle(self.elem)

        if self.elem.tag != XSD_GROUP:
            # Local group (sequence|all|choice)
            if 'name' in self.elem.attrib:
                self.parse_error("attribute 'name' not allowed for a local group")
            self._parse_content_model(self.elem)

        elif self._parse_reference():
            assert self.name is not None
            try:
                xsd_group = self.maps.lookup_group(self.name)
            except KeyError:
                self.parse_error("missing group %r" % self.prefixed_name)
                xsd_group = self.schema.create_any_content_group(parent=self)

            if isinstance(xsd_group, XsdGroup):
                self.model = xsd_group.model
                if self.model == 'all':
                    if self.max_occurs != 1:
                        self.parse_error("maxOccurs must be 1 for 'all' model groups")
                    if self.min_occurs not in (0, 1):
                        self.parse_error("minOccurs must be (0 | 1) for 'all' model groups")
                    if self.xsd_version == '1.0' and isinstance(self.parent, XsdGroup):
                        self.parse_error("in XSD 1.0 the 'all' model group cannot be nested")
                self._group.append(xsd_group)
                self.ref = xsd_group
            else:
                # Disallowed circular definition, substitute with any content group.
                self.parse_error("Circular definitions detected for group %r:" % self.name,
                                 xsd_group[0])
                self.model = 'sequence'
                self.mixed = True
                self._group.append(self.schema.xsd_any_class(ANY_ELEMENT, self.schema, self))

        else:
            attrib = self.elem.attrib
            try:
                self.name = get_qname(self.target_namespace, attrib['name'])
            except KeyError:
                pass
            else:
                if self.parent is not None:
                    self.parse_error("attribute 'name' not allowed for a local group")
                else:
                    if 'minOccurs' in attrib:
                        self.parse_error("attribute 'minOccurs' not allowed for a global group")
                    if 'maxOccurs' in attrib:
                        self.parse_error("attribute 'maxOccurs' not allowed for a global group")

                content_model = self._parse_child_component(self.elem, strict=True)
                if content_model is not None:
                    if self.parent is None:
                        if 'minOccurs' in content_model.attrib:
                            self.parse_error("attribute 'minOccurs' not allowed for the model "
                                             "of a global group", content_model)
                        if 'maxOccurs' in content_model.attrib:
                            self.parse_error("attribute 'maxOccurs' not allowed for the model "
                                             "of a global group", content_model)

                    if content_model.tag in {XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
                        self._parse_content_model(content_model)
                    else:
                        self.parse_error('unexpected tag %r' % content_model.tag, content_model)

    def _parse_content_model(self, content_model: ElementType) -> None:
        self.model = local_name(content_model.tag)
        if self.model == 'all':
            if self.max_occurs != 1:
                self.parse_error("maxOccurs must be 1 for 'all' model groups")
            if self.min_occurs not in (0, 1):
                self.parse_error("minOccurs must be (0 | 1) for 'all' model groups")

        child: ElementType
        for child in content_model:
            if child.tag == XSD_ANNOTATION or callable(child.tag):
                continue
            elif child.tag == XSD_ELEMENT:
                # Builds inner elements later, for avoid circularity.
                self.append(self.schema.xsd_element_class(child, self.schema, self, False))
            elif content_model.tag == XSD_ALL:
                self.parse_error("'all' model can contains only elements.")
            elif child.tag == XSD_ANY:
                self._group.append(XsdAnyElement(child, self.schema, self))
            elif child.tag in (XSD_SEQUENCE, XSD_CHOICE):
                self._group.append(XsdGroup(child, self.schema, self))
            elif child.tag == XSD_GROUP:
                try:
                    ref = self.schema.resolve_qname(child.attrib['ref'])
                except (KeyError, ValueError, RuntimeError) as err:
                    if 'ref' not in child.attrib:
                        self.parse_error("missing attribute 'ref' in local group", child)
                    else:
                        self.parse_error(err, child)
                    continue

                if ref != self.name:
                    xsd_group = XsdGroup(child, self.schema, self)
                    if xsd_group.model == 'all':
                        self.parse_error("'all' model can appears only at 1st level "
                                         "of a model group")
                    else:
                        self._group.append(xsd_group)
                elif self.redefine is None:
                    self.parse_error("Circular definition detected for group %r:" % self.name)
                else:
                    if child.get('minOccurs', '1') != '1' or child.get('maxOccurs', '1') != '1':
                        self.parse_error("Redefined group reference cannot have "
                                         "minOccurs/maxOccurs other than 1:")
                    self._group.append(self.redefine)

    def build(self) -> None:
        for item in self._group:
            if isinstance(item, XsdElement):
                item.build()

        if self.redefine is not None:
            for group in self.redefine.iter_components(XsdGroup):
                group.build()

    @property
    def built(self) -> bool:
        for item in self:
            if isinstance(item, XsdElement):
                if not item.built:
                    return False
            elif isinstance(item, XsdAnyElement):
                continue
            elif item.parent is None:
                continue
            elif item.parent is not self.parent and \
                    isinstance(item.parent, XsdType) and item.parent.parent is None:
                continue
            elif not item.ref and not item.built:
                return False

        return True if self.model else False

    @property
    def validation_attempted(self) -> str:
        if self.built:
            return 'full'
        elif any(item.validation_attempted == 'partial' for item in self):
            return 'partial'
        else:
            return 'none'

    @property
    def schema_elem(self) -> ElementType:
        return self.parent.elem if self.parent is not None else self.elem

    def iter_components(self, xsd_classes: Optional[ComponentClassType] = None) \
            -> Iterator[XsdComponent]:
        if xsd_classes is None or isinstance(self, xsd_classes):
            yield self
        for item in self:
            if item.parent is None:
                continue
            elif item.parent is not self.parent and isinstance(item.parent, XsdType) \
                    and item.parent.parent is None:
                continue
            yield from item.iter_components(xsd_classes)

        if self.redefine is not None and self.redefine not in self:
            yield from self.redefine.iter_components(xsd_classes)

    def admits_restriction(self, model: str) -> bool:
        if self.model == model:
            return True
        elif self.model == 'all':
            return model == 'sequence'
        elif self.model == 'choice':
            return model == 'sequence' or len(self.ref or self) <= 1
        else:
            return model == 'choice' or len(self.ref or self) <= 1

    def is_empty(self) -> bool:
        return not self.mixed and (not self._group or self.max_occurs == 0)

    def is_restriction(self, other: ModelParticleType, check_occurs: bool = True) -> bool:
        if not self._group:
            return True
        elif not isinstance(other, ParticleMixin):
            raise XMLSchemaValueError("the argument 'other' must be an XSD particle")
        elif not isinstance(other, XsdGroup):
            return self.is_element_restriction(other)
        elif not other:
            return False
        elif len(other) == other.min_occurs == other.max_occurs == 1:
            if len(self) > 1:
                return self.is_restriction(other[0], check_occurs)
            elif self.ref is None and isinstance(self[0], XsdGroup) \
                    and self[0].is_pointless(parent=self):
                return self[0].is_restriction(other[0], check_occurs)

        # Compare model with model
        if self.model != other.model and self.model != 'sequence' and \
                (len(self) > 1 or self.ref is not None and len(self[0]) > 1):
            return False
        elif self.model == other.model or other.model == 'sequence':
            return self.is_sequence_restriction(other)
        elif other.model == 'all':
            return self.is_all_restriction(other)
        else:  # other.model == 'choice':
            return self.is_choice_restriction(other)

    def is_element_restriction(self, other: ModelParticleType) -> bool:
        if self.xsd_version == '1.0' and isinstance(other, XsdElement) and \
                not other.ref and other.name not in self.schema.substitution_groups:
            return False
        elif not self.has_occurs_restriction(other):
            return False
        elif self.model == 'choice':
            if other.name in self.maps.substitution_groups and \
                    all(isinstance(e, XsdElement) and e.substitution_group == other.name
                        for e in self):
                return True
            return any(e.is_restriction(other, False) for e in self)
        else:
            min_occurs = 0
            max_occurs: Optional[int] = 0
            for item in self.iter_model():
                if isinstance(item, XsdGroup):
                    return False
                elif item.min_occurs == 0 or item.is_restriction(other, False):
                    min_occurs += item.min_occurs
                    if max_occurs is not None:
                        if item.max_occurs is None:
                            max_occurs = None
                        else:
                            max_occurs += item.max_occurs
                    continue
                return False

            if min_occurs < other.min_occurs:
                return False
            elif max_occurs is None:
                return other.max_occurs is None
            elif other.max_occurs is None:
                return True
            else:
                return max_occurs <= other.max_occurs

    def is_sequence_restriction(self, other: 'XsdGroup') -> bool:
        if not self.has_occurs_restriction(other):
            return False

        check_occurs = other.max_occurs != 0

        # Same model: declarations must simply preserve order
        other_iterator = iter(other.iter_model())
        for item in self.iter_model():
            while True:
                try:
                    other_item = next(other_iterator)
                except StopIteration:
                    return False
                if other_item is item or item.is_restriction(other_item, check_occurs):
                    break
                elif other.model == 'choice':
                    if item.max_occurs != 0:
                        continue
                    elif not other_item.is_matching(item.name, self.default_namespace):
                        continue
                    elif all(e.max_occurs == 0 for e in self.iter_model()):
                        return False
                    else:
                        break
                elif not other_item.is_emptiable():
                    return False

        if other.model == 'choice':
            return True

        while True:
            try:
                other_item = next(other_iterator)
            except StopIteration:
                return True
            else:
                if not other_item.is_emptiable():
                    return False

    def is_all_restriction(self, other: 'XsdGroup') -> bool:
        if not self.has_occurs_restriction(other):
            return False

        check_occurs = other.max_occurs != 0
        if self.ref is None:
            restriction_items = [x for x in self]
        else:
            restriction_items = [x for x in self[0]]

        for other_item in other.iter_model():
            for item in restriction_items:
                if other_item is item or item.is_restriction(other_item, check_occurs):
                    break
            else:
                if not other_item.is_emptiable():
                    return False
                continue
            restriction_items.remove(item)

        return not bool(restriction_items)

    def is_choice_restriction(self, other: 'XsdGroup') -> bool:
        if self.ref is None:
            if self.parent is None and other.parent is not None:
                return False  # not allowed restriction in XSD 1.0
            restriction_items = [x for x in self]
        elif other.parent is None:
            restriction_items = [x for x in self[0]]
        else:
            return False  # not allowed restriction in XSD 1.0

        check_occurs = other.max_occurs != 0
        max_occurs: Optional[int] = 0
        other_max_occurs: Optional[int] = 0

        for other_item in other.iter_model():
            for item in restriction_items:
                if other_item is item or item.is_restriction(other_item, check_occurs):
                    if max_occurs is not None:
                        if item.max_occurs is None:
                            max_occurs = None
                        else:
                            max_occurs += item.max_occurs

                    if other_max_occurs is not None:
                        if other_item.max_occurs is None:
                            other_max_occurs = None
                        else:
                            other_max_occurs = max(other_max_occurs, other_item.max_occurs)
                    break
            else:
                continue
            restriction_items.remove(item)

        if restriction_items:
            return False
        elif other_max_occurs is None:
            if other.max_occurs != 0:
                return True
            other_max_occurs = 0
        elif other.max_occurs is None:
            if other_max_occurs != 0:
                return True
            other_max_occurs = 0
        else:
            other_max_occurs *= other.max_occurs

        if max_occurs is None:
            return self.max_occurs == 0
        elif self.max_occurs is None:
            return max_occurs == 0
        else:
            return other_max_occurs >= max_occurs * self.max_occurs

    def check_model(self) -> None:
        """
        Checks if the model group is deterministic. Element Declarations Consistent and
        Unique Particle Attribution constraints are checked.
        :raises: an `XMLSchemaModelError` at first violated constraint.
        """
        def safe_iter_path() -> Iterator[SchemaElementType]:
            iterators: List[Iterator[ModelParticleType]] = []
            particles = iter(self)

            while True:
                try:
                    item = next(particles)
                except StopIteration:
                    try:
                        current_path.pop()
                        particles = iterators.pop()
                    except IndexError:
                        return
                else:
                    if isinstance(item, XsdGroup):
                        current_path.append(item)
                        iterators.append(particles)
                        particles = iter(item)
                        if len(iterators) > limits.MAX_MODEL_DEPTH:
                            raise XMLSchemaModelDepthError(self)
                    else:
                        yield item

        paths: Any = {}
        current_path: List[ModelParticleType] = [self]
        try:
            any_element = self.parent.open_content.any_element  # type: ignore[union-attr]
        except AttributeError:
            any_element = None

        for e in safe_iter_path():

            previous_path: List[ModelParticleType]
            for pe, previous_path in paths.values():
                # EDC check
                if not e.is_consistent(pe) or any_element and not any_element.is_consistent(pe):
                    msg = "Element Declarations Consistent violation between %r and %r: " \
                          "match the same name but with different types" % (e, pe)
                    raise XMLSchemaModelError(self, msg)

                # UPA check
                if pe is e or not pe.is_overlap(e):
                    continue
                elif pe.parent is e.parent:
                    if pe.parent.model in {'all', 'choice'}:
                        if isinstance(pe, Xsd11AnyElement) and not isinstance(e, XsdAnyElement):
                            pe.add_precedence(e, self)
                        elif isinstance(e, Xsd11AnyElement) and not isinstance(pe, XsdAnyElement):
                            e.add_precedence(pe, self)
                        else:
                            msg = "{!r} and {!r} overlap and are in the same {!r} group"
                            raise XMLSchemaModelError(self, msg.format(pe, e, pe.parent.model))
                    elif pe.min_occurs == pe.max_occurs:
                        continue

                if distinguishable_paths(previous_path + [pe], current_path + [e]):
                    continue
                elif isinstance(pe, Xsd11AnyElement) and not isinstance(e, XsdAnyElement):
                    pe.add_precedence(e, self)
                elif isinstance(e, Xsd11AnyElement) and not isinstance(pe, XsdAnyElement):
                    e.add_precedence(pe, self)
                else:
                    msg = "Unique Particle Attribution violation between {!r} and {!r}"
                    raise XMLSchemaModelError(self, msg.format(pe, e))

            paths[e.name] = e, current_path[:]

    def check_dynamic_context(self, elem: ElementType,
                              xsd_element: SchemaElementType,
                              model_element: SchemaElementType,
                              namespaces: NamespacesType) -> None:

        if model_element is not xsd_element and isinstance(model_element, XsdElement):
            if 'substitution' in model_element.block \
                    or xsd_element.type and xsd_element.type.is_blocked(model_element):
                raise XMLSchemaValidationError(
                    model_element, elem, "substitution of %r is blocked" % model_element
                )

        alternatives: Union[Tuple[()], List[XsdAlternative]] = []
        if isinstance(xsd_element, XsdAnyElement):
            if xsd_element.process_contents == 'skip':
                return

            try:
                xsd_element = self.maps.lookup_element(elem.tag)
            except LookupError:
                try:
                    type_name = elem.attrib[XSI_TYPE].strip()
                except KeyError:
                    return
                else:
                    xsd_type = self.maps.get_instance_type(
                        type_name, self.any_type, namespaces
                    )
            else:
                alternatives = xsd_element.alternatives
                try:
                    type_name = elem.attrib[XSI_TYPE].strip()
                except KeyError:
                    xsd_type = xsd_element.type
                else:
                    xsd_type = self.maps.get_instance_type(
                        type_name, xsd_element.type, namespaces
                    )

        else:
            if XSI_TYPE not in elem.attrib:
                xsd_type = xsd_element.type
            else:
                alternatives = xsd_element.alternatives
                try:
                    type_name = elem.attrib[XSI_TYPE].strip()
                except KeyError:
                    xsd_type = xsd_element.type
                else:
                    xsd_type = self.maps.get_instance_type(
                        type_name, xsd_element.type, namespaces
                    )

            if model_element is not xsd_element and \
                    isinstance(model_element, XsdElement) and model_element.block:
                for derivation in model_element.block.split():
                    if xsd_type is not model_element.type and \
                            xsd_type.is_derived(model_element.type, derivation):
                        reason = "usage of %r with type %s is blocked by head element"
                        raise XMLSchemaValidationError(
                            self, elem, reason % (xsd_element, derivation)
                        )

            if XSI_TYPE not in elem.attrib:
                return

        # If it's a restriction the context is the base_type's group
        group = self.restriction if self.restriction is not None else self

        # Dynamic EDC check of matched element
        for e in group.iter_elements():
            if not isinstance(e, XsdElement):
                continue
            elif e.name == elem.tag:
                other = e
            else:
                for other in e.iter_substitutes():
                    if other.name == elem.tag:
                        break
                else:
                    continue

            if len(other.alternatives) != len(alternatives) or \
                    not xsd_type.is_dynamic_consistent(other.type):
                reason = "%r that matches %r is not consistent with local declaration %r"
                raise XMLSchemaValidationError(self, reason % (elem, xsd_element, other))

            if not all(any(a == x for x in alternatives) for a in other.alternatives) or \
                    not all(any(a == x for x in other.alternatives) for a in alternatives):
                msg = "Maybe a not equivalent type table between elements %r and %r."
                warnings.warn(msg % (self, xsd_element), XMLSchemaTypeTableWarning, stacklevel=3)

    def match_element(self, name: str, default_namespace: Optional[str] = None) \
            -> Optional[SchemaElementType]:
        """
        Try a model-less match of a child element. Returns the
        matched element, or `None` if there is no match.
        """
        for xsd_element in self.iter_elements():
            if xsd_element.is_matching(name, default_namespace, group=self):
                return xsd_element
        return None

    def iter_decode(self, obj: ElementType, validation: str = 'lax', **kwargs: Any) \
            -> IterDecodeType[GroupDecodeType]:
        """
        Creates an iterator for decoding an Element content.

        :param obj: an Element.
        :param validation: the validation mode, can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the decoding process.
        :return: yields a list of 3-tuples (key, decoded data, decoder), \
        eventually preceded by a sequence of validation or decoding errors.
        """
        result_list: GroupDecodeType = []
        cdata_index = 1  # keys for CDATA sections are positive integers

        if not self._group and self.model == 'choice' and self.min_occurs:
            reason = "an empty 'choice' group with minOccurs > 0 cannot validate any content"
            yield self.validation_error(validation, reason, obj, **kwargs)
            yield result_list
            return

        if not self.mixed:
            # Check element CDATA
            if obj.text and obj.text.strip() or \
                    any(child.tail and child.tail.strip() for child in obj):
                if len(self) == 1 and isinstance(self[0], XsdAnyElement):
                    pass  # [XsdAnyElement()] equals to an empty complexType declaration
                else:
                    reason = "character data between child elements not allowed"
                    yield self.validation_error(validation, reason, obj, **kwargs)
                    cdata_index = 0  # Do not decode CDATA

        if cdata_index and obj.text is not None:
            text = str(obj.text.strip())
            if text:
                result_list.append((cdata_index, text, None))
                cdata_index += 1

        level = kwargs['level'] = kwargs.pop('level', 0) + 1
        over_max_depth = 'max_depth' in kwargs and kwargs['max_depth'] <= level
        if level > limits.MAX_XML_DEPTH:
            reason = "XML data depth exceeded (MAX_XML_DEPTH=%r)" % limits.MAX_XML_DEPTH
            self.validation_error('strict', reason, obj, **kwargs)

        try:
            namespaces = kwargs['namespaces']
        except KeyError:
            namespaces = default_namespace = None
        else:
            try:
                default_namespace = namespaces.get('')
            except AttributeError:
                default_namespace = None

        errors: List[Tuple[int, ModelParticleType, int, Optional[List[SchemaElementType]]]]
        xsd_element: Optional[SchemaElementType]
        expected: Optional[List[SchemaElementType]]

        model = ModelVisitor(self)
        errors = []
        broken_model = False

        for index, child in enumerate(obj):
            if callable(child.tag):
                continue  # child is a <class 'lxml.etree._Comment'>

            while model.element is not None:
                xsd_element = model.element.match(
                    child.tag, default_namespace, group=self, occurs=model.occurs
                )
                if xsd_element is None:
                    if self.interleave is not None and self.interleave.is_matching(
                            child.tag, default_namespace, self, model.occurs):
                        xsd_element = self.interleave
                        break

                    for particle, occurs, expected in model.advance(False):
                        errors.append((index, particle, occurs, expected))
                        model.clear()
                        broken_model = True  # the model is broken, continues with raw decoding.
                        xsd_element = self.match_element(child.tag, default_namespace)
                        break
                    else:
                        continue
                    break

                try:
                    self.check_dynamic_context(child, xsd_element, model.element, namespaces)
                except XMLSchemaValidationError as err:
                    yield self.validation_error(validation, err, obj, **kwargs)

                for particle, occurs, expected in model.advance(True):
                    errors.append((index, particle, occurs, expected))
                break
            else:
                if self.suffix is not None and \
                        self.suffix.is_matching(child.tag, default_namespace, self):
                    xsd_element = self.suffix
                else:
                    xsd_element = self.match_element(child.tag, default_namespace)
                    if xsd_element is None:
                        errors.append((index, self, 0, None))
                        broken_model = True
                    elif not broken_model:
                        errors.append((index, xsd_element, 0, []))
                        broken_model = True

            if xsd_element is None:
                if kwargs.get('keep_unknown') and 'converter' in kwargs:
                    for result in self.any_type.iter_decode(child, validation, **kwargs):
                        result_list.append((child.tag, result, None))
                continue
            elif 'converter' not in kwargs:
                # Validation-only mode: do not append results
                for result in xsd_element.iter_decode(child, validation, **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        yield result
                continue
            elif over_max_depth:
                if 'depth_filler' in kwargs:
                    func = kwargs['depth_filler']
                    result_list.append((child.tag, func(xsd_element), xsd_element))
                continue

            for result in xsd_element.iter_decode(child, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    yield result
                else:
                    result_list.append((child.tag, result, xsd_element))

            if cdata_index and child.tail is not None:
                tail = str(child.tail.strip())
                if tail:
                    if result_list and isinstance(result_list[-1][0], int):
                        tail = result_list[-1][1] + ' ' + tail
                        result_list[-1] = result_list[-1][0], tail, None
                    else:
                        result_list.append((cdata_index, tail, None))
                        cdata_index += 1

        if model.element is not None:
            index = len(obj)
            for particle, occurs, expected in model.stop():
                errors.append((index, particle, occurs, expected))

        if errors:
            source = kwargs.get('source')
            for index, particle, occurs, expected in errors:
                error = XMLSchemaChildrenValidationError(
                    self, obj, index, particle, occurs, expected, source, namespaces
                )
                if validation == 'strict':
                    raise error
                yield error

        yield result_list

    def iter_encode(self, obj: ElementData, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[GroupEncodeType]:
        """
        Creates an iterator for encoding data to a list containing Element data.

        :param obj: an ElementData instance.
        :param validation: the validation mode: can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the encoding process.
        :return: yields a couple with the text of the Element and a list of child \
        elements, eventually preceded by a sequence of validation errors.
        """
        level = kwargs['level'] = kwargs.get('level', 0) + 1
        errors = []
        text = raw_xml_encode(obj.text)
        children: List[ElementType] = []
        try:
            indent = kwargs['indent']
        except KeyError:
            indent = 4

        padding = '\n' + ' ' * indent * level

        try:
            converter = kwargs['converter']
        except KeyError:
            converter = kwargs['converter'] = self.schema.get_converter(**kwargs)

        default_namespace = converter.get('')
        model = ModelVisitor(self)
        index = cdata_index = 0
        wrong_content_type = False
        over_max_depth = 'max_depth' in kwargs and kwargs['max_depth'] <= level

        content: Iterable[Any]
        if obj.content is None:
            content = []
        elif isinstance(obj.content, MutableMapping) or kwargs.get('unordered'):
            content = ModelVisitor(self).iter_unordered_content(
                obj.content, default_namespace
            )
        elif not isinstance(obj.content, MutableSequence):
            wrong_content_type = True
            content = []
        elif converter.losslessly:
            content = obj.content
        else:
            content = ModelVisitor(self).iter_collapsed_content(
                obj.content, default_namespace
            )

        for index, (name, value) in enumerate(content):
            if isinstance(name, int):
                if not children:
                    text = padding + value if text is None else text + value + padding
                elif children[-1].tail is None:
                    children[-1].tail = padding + value
                else:
                    children[-1].tail += value + padding
                cdata_index += 1
                continue

            xsd_element: Optional[SchemaElementType]
            if self.interleave and self.interleave.is_matching(name, default_namespace, group=self):
                xsd_element = self.interleave
                value = get_qname(default_namespace, name), value
            else:
                while model.element is not None:
                    xsd_element = model.element.match(
                        name, default_namespace, group=self, occurs=model.occurs
                    )
                    if xsd_element is None:
                        for particle, occurs, expected in model.advance():
                            errors.append((index - cdata_index, particle, occurs, expected))
                        continue
                    elif isinstance(xsd_element, XsdAnyElement):
                        value = get_qname(default_namespace, name), value

                    for particle, occurs, expected in model.advance(True):
                        errors.append((index - cdata_index, particle, occurs, expected))
                    break
                else:
                    if self.suffix and self.suffix.is_matching(name, default_namespace, group=self):
                        xsd_element = self.suffix
                        value = get_qname(default_namespace, name), value
                    else:
                        errors.append((index - cdata_index, self, 0, []))
                        xsd_element = self.match_element(name, default_namespace)
                        if isinstance(xsd_element, XsdAnyElement):
                            value = get_qname(default_namespace, name), value
                        elif xsd_element is None:
                            if name.startswith('{') or ':' not in name:
                                reason = '{!r} does not match any declared element ' \
                                         'of the model group.'.format(name)
                            else:
                                reason = '{} has an unknown prefix {!r}'.format(
                                    name, name.split(':')[0]
                                )
                            yield self.validation_error(validation, reason, value, **kwargs)
                            continue

            if over_max_depth:
                continue

            for result in xsd_element.iter_encode(value, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    yield result
                else:
                    children.append(result)

        if model.element is not None:
            for particle, occurs, expected in model.stop():
                errors.append((index - cdata_index + 1, particle, occurs, expected))

        if children:
            if children[-1].tail is None:
                children[-1].tail = padding[:-indent] or '\n'
            else:
                children[-1].tail = children[-1].tail.strip() + (padding[:-indent] or '\n')

        cdata_not_allowed = not self.mixed and text and text.strip() and self and \
            (len(self) > 1 or not isinstance(self[0], XsdAnyElement))

        if errors or cdata_not_allowed or wrong_content_type:
            attrib = {k: raw_xml_encode(v) for k, v in obj.attributes.items()}
            elem = converter.etree_element(obj.tag, text, children, attrib)

            if wrong_content_type:
                reason = "wrong content type {!r}".format(type(obj.content))
                yield self.validation_error(validation, reason, elem, **kwargs)

            if cdata_not_allowed:
                reason = "character data between child elements not allowed"
                yield self.validation_error(validation, reason, elem, **kwargs)

            for index, particle, occurs, expected in errors:
                error = XMLSchemaChildrenValidationError(
                    validator=self,
                    elem=elem,
                    index=index,
                    particle=particle,
                    occurs=occurs,
                    expected=expected,
                    namespaces=converter.namespaces,
                )
                if validation == 'strict':
                    raise error

                error.elem = None  # replace with the element of the encoded tree
                yield error

        yield text, children class XsdElement(XsdComponent, ParticleMixin,
                 ElementPathMixin[SchemaElementType],
                 ValidationMixin[ElementType, Any]):
    """
    Class for XSD 1.0 *element* declarations.

    :ivar type: the XSD simpleType or complexType of the element.
    :ivar attributes: the group of the attributes associated with the element.

    ..  <element
          abstract = boolean : false
          block = (#all | List of (extension | restriction | substitution))
          default = string
          final = (#all | List of (extension | restriction))
          fixed = string
          form = (qualified | unqualified)
          id = ID
          maxOccurs = (nonNegativeInteger | unbounded)  : 1
          minOccurs = nonNegativeInteger : 1
          name = NCName
          nillable = boolean : false
          ref = QName
          substitutionGroup = QName
          type = QName
          {any attributes with non-schema namespace . . .}>
          Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
        </element>
    """
    name: str
    local_name: str
    qualified_name: str
    prefixed_name: str

    parent: Optional['XsdGroup']
    ref: Optional['XsdElement']
    attributes: 'XsdAttributeGroup'

    type: BaseXsdType
    abstract = False
    nillable = False
    qualified = False
    form: Optional[str] = None
    default: Optional[str] = None
    fixed: Optional[str] = None
    substitution_group: Optional[str] = None

    identities: Dict[str, XsdIdentity]
    alternatives = ()  # type: Union[Tuple[()], List[XsdAlternative]]
    inheritable = ()  # type: Union[Tuple[()], Dict[str, XsdAttribute]]

    _ADMITTED_TAGS = {XSD_ELEMENT}
    _block: Optional[str] = None
    _final: Optional[str] = None
    _head_type = None
    _build = True

    binding: Optional[DataBindingType] = None

    def __init__(self, elem: etree_element,
                 schema: SchemaType,
                 parent: Optional[XsdComponent] = None,
                 build: bool = True) -> None:

        if not build:
            self._build = False
        super(XsdElement, self).__init__(elem, schema, parent)

    def __repr__(self) -> str:
        return '%s(%s=%r, occurs=%r)' % (
            self.__class__.__name__,
            'name' if self.ref is None else 'ref',
            self.prefixed_name,
            list(self.occurs)
        )

    def __setattr__(self, name: str, value: Any) -> None:
        if name == "type":
            if isinstance(value, XsdSimpleType):
                self.attributes = self.schema.create_empty_attribute_group(self)
            else:
                self.attributes = value.attributes
        super(XsdElement, self).__setattr__(name, value)

    def __iter__(self) -> Iterator[SchemaElementType]:
        if self.type.has_complex_content():
            yield from self.type.content.iter_elements()  # type: ignore[union-attr]

    def _parse(self) -> None:
        if not self._build:
            return

        self._parse_particle(self.elem)
        self._parse_attributes()

        if self.ref is None:
            self._parse_type()
            self._parse_constraints()

            if self.parent is None and 'substitutionGroup' in self.elem.attrib:
                self._parse_substitution_group(self.elem.attrib['substitutionGroup'])

    def _parse_attributes(self) -> None:
        attrib = self.elem.attrib
        if self._parse_reference():
            try:
                xsd_element: XsdElement = self.maps.lookup_element(self.name)
            except KeyError:
                self.type = self.any_type
                self.parse_error('unknown element %r' % self.name)
            else:
                self.ref = xsd_element
                self.type = xsd_element.type
                self.abstract = xsd_element.abstract
                self.nillable = xsd_element.nillable
                self.qualified = xsd_element.qualified
                self.form = xsd_element.form
                self.default = xsd_element.default
                self.fixed = xsd_element.fixed
                self.substitution_group = xsd_element.substitution_group
                self.identities = xsd_element.identities
                self.alternatives = xsd_element.alternatives

            for attr_name in {'type', 'nillable', 'default', 'fixed', 'form',
                              'block', 'abstract', 'final', 'substitutionGroup'}:
                if attr_name in attrib:
                    msg = "attribute {!r} is not allowed when element reference is used"
                    self.parse_error(msg.format(attr_name))
            return

        if 'form' in attrib:
            self.form = attrib['form']
            if self.form == 'qualified':
                self.qualified = True
        elif self.schema.element_form_default == 'qualified':
            self.qualified = True

        try:
            if self.parent is None or self.qualified:
                self.name = get_qname(self.target_namespace, attrib['name'])
            else:
                self.name = attrib['name']
        except KeyError:
            pass

        if 'abstract' in attrib:
            if self.parent is not None:
                self.parse_error("local scope elements cannot have abstract attribute")
            if attrib['abstract'].strip() in {'true', '1'}:
                self.abstract = True

        if 'block' in attrib:
            try:
                self._block = get_xsd_derivation_attribute(
                    self.elem, 'block', XSD_ELEMENT_DERIVATIONS
                )
            except ValueError as err:
                self.parse_error(err)

        if 'nillable' in attrib and attrib['nillable'].strip() in {'true', '1'}:
            self.nillable = True

        if self.parent is None:
            if 'final' in attrib:
                try:
                    self._final = get_xsd_derivation_attribute(
                        self.elem, 'final', XSD_TYPE_DERIVATIONS
                    )
                except ValueError as err:
                    self.parse_error(err)

            for attr_name in {'ref', 'form', 'minOccurs', 'maxOccurs'}:
                if attr_name in attrib:
                    msg = "attribute {!r} is not allowed in a global element declaration"
                    self.parse_error(msg.format(attr_name))
        else:
            for attr_name in {'final', 'substitutionGroup'}:
                if attr_name in attrib:
                    msg = "attribute {!r} not allowed in a local element declaration"
                    self.parse_error(msg.format(attr_name))

    def _parse_type(self) -> None:
        type_name = self.elem.get('type')
        if type_name is not None:
            try:
                extended_name = self.schema.resolve_qname(type_name)
            except (KeyError, ValueError, RuntimeError) as err:
                self.parse_error(err)
                self.type = self.any_type
            else:
                if extended_name == XSD_ANY_TYPE:
                    self.type = self.any_type
                else:
                    try:
                        self.type = self.maps.lookup_type(extended_name)
                    except KeyError:
                        self.parse_error('unknown type {!r}'.format(type_name))
                        self.type = self.any_type
            finally:
                child = self._parse_child_component(self.elem, strict=False)
                if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE):
                    self.parse_error("the attribute 'type' and a {} local declaration "
                                     "are mutually exclusive".format(child.tag.split('}')[-1]))
        else:
            child = self._parse_child_component(self.elem, strict=False)
            if child is None:
                self.type = self.any_type
            elif child.tag == XSD_COMPLEX_TYPE:
                self.type = self.schema.xsd_complex_type_class(child, self.schema, self)
            elif child.tag == XSD_SIMPLE_TYPE:
                self.type = self.schema.simple_type_factory(child, self.schema, self)
            else:
                self.type = self.any_type

    def _parse_constraints(self) -> None:
        # Value constraints
        if 'default' in self.elem.attrib:
            self.default = self.elem.attrib['default']
            if 'fixed' in self.elem.attrib:
                self.parse_error("'default' and 'fixed' attributes are mutually exclusive")

            if not self.type.is_valid(self.default):
                msg = "'default' value {!r} is not compatible with element's type"
                self.parse_error(msg.format(self.default))
                self.default = None
            elif self.xsd_version == '1.0' and self.type.is_key():
                self.parse_error("xs:ID or a type derived from xs:ID "
                                 "cannot have a default value")

        elif 'fixed' in self.elem.attrib:
            self.fixed = self.elem.attrib['fixed']
            if not self.type.is_valid(self.fixed):
                msg = "'fixed' value {!r} is not compatible with element's type"
                self.parse_error(msg.format(self.fixed))
                self.fixed = None
            elif self.xsd_version == '1.0' and self.type.is_key():
                self.parse_error("xs:ID or a type derived from xs:ID "
                                 "cannot have a fixed value")

        # Identity constraints
        self.identities = {}
        constraint: Union[XsdKey, XsdUnique, XsdKeyref]
        for child in self.elem:
            if child.tag == XSD_UNIQUE:
                constraint = self.schema.xsd_unique_class(child, self.schema, self)
            elif child.tag == XSD_KEY:
                constraint = self.schema.xsd_key_class(child, self.schema, self)
            elif child.tag == XSD_KEYREF:
                constraint = self.schema.xsd_keyref_class(child, self.schema, self)
            else:
                # Invalid tags already caught by validation against the meta-schema
                continue

            if constraint.ref:
                if constraint.name in self.identities:
                    self.parse_error("duplicated identity constraint %r:" % constraint.name, child)
                self.identities[constraint.name] = constraint
                continue

            try:
                if child != self.maps.identities[constraint.name].elem:
                    self.parse_error("duplicated identity constraint %r:" % constraint.name, child)
            except KeyError:
                self.maps.identities[constraint.name] = constraint
            finally:
                self.identities[constraint.name] = constraint

    def _parse_substitution_group(self, substitution_group: str) -> None:
        try:
            substitution_group_qname = self.schema.resolve_qname(substitution_group)
        except (KeyError, ValueError, RuntimeError) as err:
            self.parse_error(err)
            return
        else:
            if substitution_group_qname[0] != '{':
                substitution_group_qname = get_qname(
                    self.target_namespace, substitution_group_qname
                )

        try:
            head_element = self.maps.lookup_element(substitution_group_qname)
        except KeyError:
            self.parse_error("unknown substitutionGroup %r" % substitution_group)
            return
        else:
            if isinstance(head_element, tuple):
                self.parse_error("circularity found for substitutionGroup %r" % substitution_group)
                return
            elif 'substitution' in head_element.block:
                return

        final = head_element.final
        if self.type == head_element.type:
            pass
        elif self.type.name == XSD_ANY_TYPE:
            if head_element.type.name != XSD_ANY_TYPE:
                # Use head element's type for validate content
                # ref: https://www.w3.org/TR/xmlschema-1/#cElement_Declarations
                self._head_type = head_element.type
        elif not self.type.is_derived(head_element.type):
            self.parse_error("%r type is not of the same or a derivation "
                             "of the head element %r type." % (self, head_element))
        elif final == '#all' or 'extension' in final and 'restriction' in final:
            self.parse_error("head element %r can't be substituted by an element "
                             "that has a derivation of its type" % head_element)
        elif 'extension' in final and self.type.is_derived(head_element.type, 'extension'):
            self.parse_error("head element %r can't be substituted by an element "
                             "that has an extension of its type" % head_element)
        elif 'restriction' in final and self.type.is_derived(head_element.type, 'restriction'):
            self.parse_error("head element %r can't be substituted by an element "
                             "that has a restriction of its type" % head_element)

        try:
            self.maps.substitution_groups[substitution_group_qname].add(self)
        except KeyError:
            self.maps.substitution_groups[substitution_group_qname] = {self}
        finally:
            self.substitution_group = substitution_group_qname

    @property
    def xpath_proxy(self) -> XMLSchemaProxy:
        return XMLSchemaProxy(
            schema=cast(XMLSchemaProtocol, self.schema),
            base_element=cast(ElementProtocol, self)
        )

    def build(self) -> None:
        if self._build:
            return
        self._build = True
        self._parse()

    @property
    def built(self) -> bool:
        return hasattr(self, 'type') and \
            (self.type.parent is None or self.type.built) and \
            all(c.built for c in self.identities.values())

    @property
    def validation_attempted(self) -> str:
        if self.built:
            return 'full'
        elif self.type.validation_attempted == 'partial':
            return 'partial'
        elif any(c.validation_attempted == 'partial' for c in self.identities.values()):
            return 'partial'
        else:
            return 'none'

    @property
    def scope(self) -> str:
        """The scope of the element declaration that can be 'global' or 'local'."""
        return 'global' if self.parent is None else 'local'

    @property
    def value_constraint(self) -> Optional[str]:
        """The fixed or the default value if either is defined, `None` otherwise."""
        return self.fixed if self.fixed is not None else self.default

    @property
    def final(self) -> str:
        if self.ref is not None:
            return self.ref.final
        elif self._final is not None:
            return self._final
        return self.schema.final_default

    @property
    def block(self) -> str:
        if self.ref is not None:
            return self.ref.block
        elif self._block is not None:
            return self._block
        return self.schema.block_default

    def get_binding(self, *bases: Type[Any], replace_existing: bool = False, **attrs: Any) \
            -> DataBindingType:
        """
        Gets data object binding for XSD element, creating a new one if it doesn't exist.

        :param bases: base classes to use for creating the binding class.
        :param replace_existing: provide `True` to replace an existing binding class.
        :param attrs: attribute and method definitions for the binding class body.
        """
        if self.binding is None or not replace_existing:
            if not bases:
                bases = (dataobjects.DataElement,)
            attrs['xsd_element'] = self
            class_name = '{}Binding'.format(self.local_name.title().replace('_', ''))
            self.binding = cast(DataBindingType,
                                dataobjects.DataBindingMeta(class_name, bases, attrs))
        return self.binding

    def get_attribute(self, name: str) -> Optional[XsdAttribute]:
        if name[0] != '{':
            name = get_qname(self.type.target_namespace, name)
        if not isinstance(self.type, XsdSimpleType):
            xsd_attribute = self.type.attributes[name]
            assert isinstance(xsd_attribute, XsdAttribute)
            return xsd_attribute
        return None

    def get_type(self, elem: Union[ElementType, ElementData],
                 inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType:
        return self._head_type or self.type

    def get_attributes(self, xsd_type: BaseXsdType) -> 'XsdAttributeGroup':
        if not isinstance(xsd_type, XsdSimpleType):
            return xsd_type.attributes
        elif xsd_type is self.type:
            return self.attributes
        else:
            return self.schema.create_empty_attribute_group(self)

    def get_path(self, ancestor: Optional[XsdComponent] = None,
                 reverse: bool = False) -> Optional[str]:
        """
        Returns the XPath expression of the element. The path is relative to the schema instance
        in which the element is contained or is relative to a specific ancestor passed as argument.
        In the latter case returns `None` if the argument is not an ancestor.

        :param ancestor: optional XSD component of the same schema, that maybe \
        an ancestor of the element.
        :param reverse: if set to `True` returns the reverse path, from the element to ancestor.
        """
        path: List[str] = []
        xsd_component: Optional[XsdComponent] = self
        while xsd_component is not None:
            if xsd_component is ancestor:
                return '/'.join(reversed(path)) or '.'
            elif isinstance(xsd_component, XsdElement):
                path.append('..' if reverse else xsd_component.name)
            xsd_component = xsd_component.parent
        else:
            if ancestor is None:
                return '/'.join(reversed(path)) or '.'
            return None

    def iter_components(self, xsd_classes: Optional[ComponentClassType] = None) \
            -> Iterator[XsdComponent]:

        if xsd_classes is None:
            yield self
            yield from self.identities.values()
        else:
            if isinstance(self, xsd_classes):
                yield self
            if issubclass(XsdIdentity, xsd_classes):
                yield from self.identities.values()

        if self.ref is None and self.type.parent is not None:
            yield from self.type.iter_components(xsd_classes)

    def iter_substitutes(self) -> Iterator['XsdElement']:
        if self.parent is None or self.ref is not None:
            for xsd_element in self.maps.substitution_groups.get(self.name, ()):
                if not xsd_element.abstract:
                    yield xsd_element
                for e in xsd_element.iter_substitutes():
                    if not e.abstract:
                        yield e

    def data_value(self, elem: ElementType) -> Optional[AtomicValueType]:
        """Returns the decoded data value of the provided element as XPath fn:data()."""
        text = elem.text
        if text is None:
            text = self.fixed if self.fixed is not None else self.default
            if text is None:
                return None
        return self.type.text_decode(text)

    def check_dynamic_context(self, elem: ElementType, **kwargs: Any) -> None:
        try:
            locations = kwargs['locations']
        except KeyError:
            return

        schema: Optional[SchemaType]
        for ns, url in etree_iter_location_hints(elem):
            if ns not in locations:
                locations[ns] = url
            elif locations[ns] is None:
                reason = "schemaLocation declaration after namespace start"
                raise XMLSchemaValidationError(self, elem, reason)

            if ns == self.target_namespace:
                schema = self.schema.include_schema(url, self.schema.base_url)
            else:
                schema = self.schema.import_schema(ns, url, self.schema.base_url)

            if schema is None:
                reason = f"missing dynamic loaded schema from {url}"
                raise XMLSchemaValidationError(self, elem, reason)
            elif not schema.built:
                reason = "dynamic loaded schema change the assessment"
                raise XMLSchemaValidationError(self, elem, reason)

        if elem.attrib:
            for name in elem.attrib:
                if name[0] == '{':
                    ns = get_namespace(name)
                    if ns not in locations:
                        locations[ns] = None

        if elem.tag[0] == '{':
            ns = get_namespace(elem.tag)
            if ns not in locations:
                locations[ns] = None

    def start_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None:
        """
        Start tracking of XSD element's identities.

        :param identities: a dictionary containing the identities counters.
        """
        for constraint in self.identities.values():
            try:
                identities[constraint].clear()
            except KeyError:
                identities[constraint] = constraint.get_counter()

    def stop_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None:
        """
        Stop tracking of XSD element's identities.

        :param identities: a dictionary containing the identities counters.
        """
        for identity in self.identities.values():
            try:
                identities[identity].enabled = False
            except KeyError:
                identities[identity] = identity.get_counter(enabled=False)

    def iter_decode(self, obj: ElementType, validation: str = 'lax', **kwargs: Any) \
            -> IterDecodeType[Any]:
        """
        Creates an iterator for decoding an Element instance.

        :param obj: the Element that has to be decoded.
        :param validation: the validation mode, can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the decoding process.
        :return: yields a decoded object, eventually preceded by a sequence of \
        validation or decoding errors.
        """
        if self.abstract:
            reason = "cannot use an abstract element for validation"
            yield self.validation_error(validation, reason, obj, **kwargs)

        try:
            namespaces = kwargs['namespaces']
        except KeyError:
            namespaces = None

        try:
            level = kwargs['level']
        except KeyError:
            level = kwargs['level'] = 0

        try:
            identities = kwargs['identities']
        except KeyError:
            identities = kwargs['identities'] = {}

        self.start_identities(identities)

        try:
            converter = kwargs['converter']
        except KeyError:
            converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
        else:
            if converter is not None and not isinstance(converter, XMLSchemaConverter):
                converter = kwargs['converter'] = self.schema.get_converter(**kwargs)

        try:
            pass  # self.check_dynamic_context(elem, **kwargs) TODO: dynamic schema load
        except XMLSchemaValidationError as err:
            yield self.validation_error(validation, err, obj, **kwargs)

        inherited = kwargs.get('inherited')
        value = content = attributes = None
        nilled = False

        # Get the instance effective type
        xsd_type = self.get_type(obj, inherited)
        if XSI_TYPE in obj.attrib:
            type_name = obj.attrib[XSI_TYPE].strip()
            try:
                xsd_type = self.maps.get_instance_type(type_name, xsd_type, namespaces)
            except (KeyError, TypeError) as err:
                yield self.validation_error(validation, err, obj, **kwargs)
            else:
                if self.identities:
                    xpath_element = XPathElement(self.name, xsd_type)
                    for identity in self.identities.values():
                        if isinstance(identity.elements, tuple):
                            continue  # Skip unbuilt identities

                        context = IdentityXPathContext(
                            self.schema, item=xpath_element  # type: ignore[arg-type]
                        )
                        for e in identity.selector.token.select_results(context):
                            if not isinstance(e, XsdElement):
                                reason = "selector xpath expression can only select elements"
                                yield self.validation_error(validation, reason, e, **kwargs)
                            elif e not in identity.elements:
                                identity.elements[e] = None

            if xsd_type.is_blocked(self):
                reason = "usage of %r is blocked" % xsd_type
                yield self.validation_error(validation, reason, obj, **kwargs)

        if xsd_type.abstract:
            yield self.validation_error(validation, "%r is abstract", obj, **kwargs)
        if xsd_type.is_complex() and self.xsd_version == '1.1':
            kwargs['id_list'] = []  # Track XSD 1.1 multiple xs:ID attributes/children

        content_decoder = xsd_type if isinstance(xsd_type, XsdSimpleType) else xsd_type.content

        # Decode attributes
        attribute_group = self.get_attributes(xsd_type)
        result: Any
        for result in attribute_group.iter_decode(obj.attrib, validation, **kwargs):
            if isinstance(result, XMLSchemaValidationError):
                yield self.validation_error(validation, result, obj, **kwargs)
            else:
                attributes = result

        if self.inheritable and any(name in self.inheritable for name in obj.attrib):
            if inherited:
                inherited = inherited.copy()
                inherited.update((k, v) for k, v in obj.attrib.items() if k in self.inheritable)
            else:
                inherited = {k: v for k, v in obj.attrib.items() if k in self.inheritable}
            kwargs['inherited'] = inherited

        # Checks the xsi:nil attribute of the instance
        if XSI_NIL in obj.attrib:
            xsi_nil = obj.attrib[XSI_NIL].strip()
            if not self.nillable:
                reason = "element is not nillable."
                yield self.validation_error(validation, reason, obj, **kwargs)
            elif xsi_nil not in {'0', '1', 'false', 'true'}:
                reason = "xsi:nil attribute must have a boolean value."
                yield self.validation_error(validation, reason, obj, **kwargs)
            elif xsi_nil in ('0', 'false'):
                pass
            elif self.fixed is not None:
                reason = "xsi:nil='true' but the element has a fixed value."
                yield self.validation_error(validation, reason, obj, **kwargs)
            elif obj.text is not None or len(obj):
                reason = "xsi:nil='true' but the element is not empty."
                yield self.validation_error(validation, reason, obj, **kwargs)
            else:
                nilled = True

        if xsd_type.is_empty() and obj.text and xsd_type.normalize(obj.text):
            reason = "character data is not allowed because content is empty"
            yield self.validation_error(validation, reason, obj, **kwargs)

        if nilled:
            pass
        elif not isinstance(content_decoder, XsdSimpleType):
            if not isinstance(xsd_type, XsdSimpleType):
                for assertion in xsd_type.assertions:
                    for error in assertion(obj, **kwargs):
                        yield self.validation_error(validation, error, **kwargs)

            for result in content_decoder.iter_decode(obj, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    yield self.validation_error(validation, result, obj, **kwargs)
                else:
                    content = result

            if content and len(content) == 1 and content[0][0] == 1:
                value, content = content[0][1], None

            if self.fixed is not None and \
                    (len(obj) > 0 or value is not None and self.fixed != value):
                reason = "must have the fixed value %r" % self.fixed
                yield self.validation_error(validation, reason, obj, **kwargs)

        else:
            if len(obj):
                reason = "a simple content element can't have child elements."
                yield self.validation_error(validation, reason, obj, **kwargs)

            text = obj.text
            if self.fixed is not None:
                if text is None:
                    text = self.fixed
                elif text == self.fixed:
                    pass
                elif not strictly_equal(xsd_type.text_decode(text),
                                        xsd_type.text_decode(self.fixed)):
                    reason = "must have the fixed value %r" % self.fixed
                    yield self.validation_error(validation, reason, obj, **kwargs)

            elif not text and self.default is not None and kwargs.get('use_defaults'):
                text = self.default

            if not isinstance(xsd_type, XsdSimpleType):
                for assertion in xsd_type.assertions:
                    for error in assertion(obj, value=text, **kwargs):
                        yield self.validation_error(validation, error, **kwargs)

                if text and content_decoder.is_list():
                    value = text.split()
                else:
                    value = text

            elif xsd_type.is_notation():
                if xsd_type.name == XSD_NOTATION_TYPE:
                    msg = "cannot validate against xs:NOTATION directly, " \
                          "only against a subtype with an enumeration facet"
                    yield self.validation_error(validation, msg, text, **kwargs)
                elif not xsd_type.enumeration:
                    msg = "missing enumeration facet in xs:NOTATION subtype"
                    yield self.validation_error(validation, msg, text, **kwargs)

            if text is None:
                for result in content_decoder.iter_decode('', validation, **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        yield self.validation_error(validation, result, obj, **kwargs)
                        if 'filler' in kwargs:
                            value = kwargs['filler'](self)
            else:
                for result in content_decoder.iter_decode(text, validation, **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        yield self.validation_error(validation, result, obj, **kwargs)
                    elif result is None and 'filler' in kwargs:
                        value = kwargs['filler'](self)
                    else:
                        value = result

            if 'value_hook' in kwargs:
                value = kwargs['value_hook'](value, xsd_type)
            elif isinstance(value, (int, float, list)) or value is None:
                pass
            elif isinstance(value, str):
                if value.startswith('{') and xsd_type.is_qname():
                    value = text
            elif isinstance(value, Decimal):
                try:
                    value = kwargs['decimal_type'](value)
                except (KeyError, TypeError):
                    pass
            elif isinstance(value, (AbstractDateTime, Duration)):
                if not kwargs.get('datetime_types'):
                    value = str(value) if text is None else text.strip()
            elif isinstance(value, AbstractBinary):
                if not kwargs.get('binary_types'):
                    value = str(value)

        if converter is not None:
            element_data = ElementData(obj.tag, value, content, attributes)
            yield converter.element_decode(element_data, self, xsd_type, level)
        elif not level:
            yield ElementData(obj.tag, value, None, attributes)

        if content is not None:
            del content

        # Collects fields values for identities that refer to this element.
        for identity, counter in identities.items():
            if not counter.enabled or not identity.elements:
                continue
            elif self in identity.elements:
                xsd_element = self
            elif self.ref in identity.elements:
                xsd_element = self.ref
            else:
                continue

            try:
                xsd_fields: Optional[IdentityCounterType]
                if xsd_type is self.type:
                    xsd_fields = identity.elements[xsd_element]
                    if xsd_fields is None:
                        xsd_fields = identity.get_fields(xsd_element)
                        identity.elements[xsd_element] = xsd_fields
                else:
                    xsd_element = cast(XsdElement, self.copy())
                    xsd_element.type = xsd_type
                    xsd_fields = identity.get_fields(xsd_element)

                if all(x is None for x in xsd_fields):
                    continue
                decoders = cast(Tuple[XsdAttribute, ...], xsd_fields)
                fields = identity.get_fields(obj, namespaces, decoders=decoders)
            except (XMLSchemaValueError, XMLSchemaTypeError) as err:
                yield self.validation_error(validation, err, obj, **kwargs)
            else:
                if any(x is not None for x in fields) or nilled:
                    try:
                        counter.increase(fields)
                    except ValueError as err:
                        yield self.validation_error(validation, err, obj, **kwargs)

        # Apply non XSD optional validations
        if 'extra_validator' in kwargs:
            try:
                result = kwargs['extra_validator'](obj, self)
            except XMLSchemaValidationError as err:
                yield self.validation_error(validation, err, obj, **kwargs)
            else:
                if isinstance(result, GeneratorType):
                    for error in result:
                        yield self.validation_error(validation, error, obj, **kwargs)

        # Disable collect for out of scope identities and check key references
        if 'max_depth' not in kwargs:
            for identity in self.identities.values():
                counter = identities[identity]
                counter.enabled = False
                if isinstance(identity, XsdKeyref):
                    for error in counter.iter_errors(identities):
                        yield self.validation_error(validation, error, obj, **kwargs)
        elif level:
            self.stop_identities(identities)

    def to_objects(self, obj: ElementType, with_bindings: bool = False, **kwargs: Any) \
            -> DecodeType['dataobjects.DataElement']:
        """
        Decodes XML data to Python data objects.

        :param obj: the XML data source.
        :param with_bindings: if `True` is provided the decoding is done using \
        :class:`DataBindingConverter` that used XML data binding classes. For \
        default the objects are instances of :class:`DataElement` and uses the \
        :class:`DataElementConverter`.
        :param kwargs: other optional keyword arguments for the method \
        :func:`iter_decode`, except the argument *converter*.
        """
        if with_bindings:
            return self.decode(obj, converter=dataobjects.DataBindingConverter, **kwargs)
        return self.decode(obj, converter=dataobjects.DataElementConverter, **kwargs)

    def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \
            -> IterEncodeType[ElementType]:
        """
        Creates an iterator for encoding data to an Element.

        :param obj: the data that has to be encoded.
        :param validation: the validation mode: can be 'lax', 'strict' or 'skip'.
        :param kwargs: keyword arguments for the encoding process.
        :return: yields an Element, eventually preceded by a sequence of \
        validation or encoding errors.
        """
        errors: List[Union[str, Exception]] = []

        try:
            converter = kwargs['converter']
        except KeyError:
            converter = kwargs['converter'] = self.schema.get_converter(**kwargs)
        else:
            if not isinstance(converter, XMLSchemaConverter):
                converter = kwargs['converter'] = self.schema.get_converter(**kwargs)

        try:
            level = kwargs['level']
        except KeyError:
            level = 0
            element_data = converter.element_encode(obj, self, level)
            if not self.is_matching(element_data.tag, self.default_namespace):
                errors.append("data tag does not match XSD element name")

            if 'max_depth' in kwargs and kwargs['max_depth'] == 0:
                for e in errors:
                    yield self.validation_error(validation, e, **kwargs)
                return
        else:
            element_data = converter.element_encode(obj, self, level)

        text = None
        children = element_data.content
        attributes = ()

        xsd_type = self.get_type(element_data)
        if XSI_TYPE in element_data.attributes:
            type_name = element_data.attributes[XSI_TYPE].strip()
            try:
                xsd_type = self.maps.get_instance_type(type_name, xsd_type, converter)
            except (KeyError, TypeError) as err:
                errors.append(err)
            else:
                default_namespace = converter.get('')
                if default_namespace and not isinstance(xsd_type, XsdSimpleType):
                    # Adjust attributes mapped into default namespace

                    ns_part = '{%s}' % default_namespace
                    for k in list(element_data.attributes):
                        if not k.startswith(ns_part):
                            continue
                        elif k in xsd_type.attributes:
                            continue

                        local_name = k[len(ns_part):]
                        if local_name in xsd_type.attributes:
                            element_data.attributes[local_name] = element_data.attributes[k]
                            del element_data.attributes[k]

        attribute_group = self.get_attributes(xsd_type)
        result: Any
        for result in attribute_group.iter_encode(element_data.attributes, validation, **kwargs):
            if isinstance(result, XMLSchemaValidationError):
                errors.append(result)
            else:
                attributes = result

        if XSI_NIL in element_data.attributes:
            xsi_nil = element_data.attributes[XSI_NIL].strip()
            if not self.nillable:
                errors.append("element is not nillable.")
            elif xsi_nil not in {'0', '1', 'true', 'false'}:
                errors.append("xsi:nil attribute must has a boolean value.")
            elif xsi_nil in ('0', 'false'):
                pass
            elif self.fixed is not None:
                errors.append("xsi:nil='true' but the element has a fixed value.")
            elif element_data.text is not None or element_data.content:
                errors.append("xsi:nil='true' but the element is not empty.")
            else:
                elem = converter.etree_element(element_data.tag, attrib=attributes, level=level)
                for e in errors:
                    yield self.validation_error(validation, e, elem, **kwargs)
                yield elem
                return

        if isinstance(xsd_type, XsdSimpleType):
            if element_data.content:
                errors.append("a simpleType element can't has child elements.")

            if element_data.text is not None:
                for result in xsd_type.iter_encode(element_data.text, validation, **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        errors.append(result)
                    else:
                        text = result

            elif self.fixed is not None:
                text = self.fixed
            elif self.default is not None and kwargs.get('use_defaults'):
                text = self.default

        elif xsd_type.has_simple_content():
            if element_data.text is not None:
                for result in xsd_type.content.iter_encode(element_data.text,
                                                           validation, **kwargs):
                    if isinstance(result, XMLSchemaValidationError):
                        errors.append(result)
                    else:
                        text = result

            elif self.fixed is not None:
                text = self.fixed
            elif self.default is not None and kwargs.get('use_defaults'):
                text = self.default

        else:
            for result in xsd_type.content.iter_encode(element_data, validation, **kwargs):
                if isinstance(result, XMLSchemaValidationError):
                    errors.append(result)
                elif result:
                    text, children = result

        elem = converter.etree_element(element_data.tag, text, children, attributes, level)

        if errors:
            for e in errors:
                yield self.validation_error(validation, e, elem, **kwargs)
        yield elem
        del element_data

    def is_matching(self, name: Optional[str], default_namespace: Optional[str] = None,
                    group: Optional['XsdGroup'] = None, **kwargs: Any) -> bool:
        if not name:
            return False
        elif default_namespace and name[0] != '{':
            qname = '{%s}%s' % (default_namespace, name)
            if name == self.name or qname == self.name:
                return True
            return any(name == e.name or qname == e.name for e in self.iter_substitutes())
        elif name == self.name:
            return True
        else:
            return any(name == e.name for e in self.iter_substitutes())

    def match(self, name: Optional[str], default_namespace: Optional[str] = None,
              **kwargs: Any) -> Optional['XsdElement']:
        if not name:
            return None
        elif default_namespace and name[0] != '{':
            qname = '{%s}%s' % (default_namespace, name)
            if name == self.name or qname == self.name:
                return self

            for xsd_element in self.iter_substitutes():
                if name == xsd_element.name or qname == xsd_element.name:
                    return xsd_element

        elif name == self.name:
            return self
        else:
            for xsd_element in self.iter_substitutes():
                if name == xsd_element.name:
                    return xsd_element
        return None

    def is_restriction(self, other: ModelParticleType, check_occurs: bool = True) -> bool:
        e: ModelParticleType

        if isinstance(other, XsdAnyElement):
            if self.min_occurs == self.max_occurs == 0:
                return True
            if check_occurs and not self.has_occurs_restriction(other):
                return False
            return other.is_matching(self.name, self.default_namespace)
        elif isinstance(other, XsdElement):
            if self.name != other.name:
                if other.name == self.substitution_group and \
                        other.min_occurs != other.max_occurs and \
                        self.max_occurs != 0 and not other.abstract \
                        and self.xsd_version == '1.0':
                    # An UPA violation case. Base is the head element, it's not
                    # abstract and has non deterministic occurs: this is less
                    # restrictive than W3C test group (elemZ026), marked as
                    # invalid despite it's based on an abstract declaration.
                    # See also test case invalid_restrictions1.xsd.
                    return False

                for e in other.iter_substitutes():
                    if e.name == self.name:
                        break
                else:
                    return False

            if check_occurs and not self.has_occurs_restriction(other):
                return False
            elif not self.is_consistent(other) and self.type.elem is not other.type.elem and \
                    not self.type.is_derived(other.type, 'restriction') and not other.type.abstract:
                return False
            elif other.fixed is not None and \
                    (self.fixed is None or self.type.normalize(
                        self.fixed) != other.type.normalize(other.fixed)):
                return False
            elif other.nillable is False and self.nillable:
                return False
            elif any(value not in self.block for value in other.block.split()):
                return False
            elif not all(k in other.identities for k in self.identities):
                return False
            else:
                return True
        elif other.model == 'choice':
            if other.is_empty() and self.max_occurs != 0:
                return False

            check_group_items_occurs = self.xsd_version == '1.0'
            total_occurs = OccursCalculator()
            for e in other.iter_model():
                if not isinstance(e, (XsdElement, XsdAnyElement)):
                    return False
                elif not self.is_restriction(e, check_group_items_occurs):
                    continue
                total_occurs += e
                total_occurs *= other
                if self.has_occurs_restriction(total_occurs):
                    return True
                total_occurs.reset()
            return False
        else:
            match_restriction = False
            for e in other.iter_model():
                if match_restriction:
                    if not e.is_emptiable():
                        return False
                elif self.is_restriction(e):
                    match_restriction = True
                elif not e.is_emptiable():
                    return False
            return True

    def is_overlap(self, other: SchemaElementType) -> bool:
        if isinstance(other, XsdElement):
            if self.name == other.name:
                return True
            elif other.substitution_group == self.name or other.name == self.substitution_group:
                return True
        elif isinstance(other, XsdAnyElement):
            if other.is_matching(self.name, self.default_namespace):
                return True
            for e in self.maps.substitution_groups.get(self.name, ()):
                if other.is_matching(e.name, self.default_namespace):
                    return True
        return False

    def is_consistent(self, other: SchemaElementType, strict: bool = True) -> bool:
        """
        Element Declarations Consistent check between two element particles.

        Ref: https://www.w3.org/TR/xmlschema-1/#cos-element-consistent

        :returns: `True` if there is no inconsistency between the particles, `False` otherwise,
        """
        return self.name != other.name or self.type is other.type

    def is_single(self) -> bool:
        if self.parent is None:
            return True
        elif self.max_occurs != 1:
            return False
        elif self.parent.max_occurs == 1:
            return True
        else:
            return self.parent.model != 'choice' and len(self.parent) > 1

    def is_empty(self) -> bool:
        return self.fixed == '' or self.type.is_empty()
import warnings
from collections import Counter
from functools import lru_cache
from typing import cast, Any, Callable, Dict, List, Iterable, Iterator, \
    MutableMapping, Optional, Set, Union, Tuple, Type
from ..exceptions import XMLSchemaKeyError, XMLSchemaTypeError, XMLSchemaValueError, \
    XMLSchemaRuntimeError, XMLSchemaWarning
from ..names import XSD_NAMESPACE, XSD_REDEFINE, XSD_OVERRIDE, XSD_NOTATION, \
    XSD_ANY_TYPE, XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_GROUP, \
    XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ELEMENT, XSI_TYPE
from ..aliases import ComponentClassType, ElementType, SchemaType, BaseXsdType, \
    SchemaGlobalType
from ..helpers import get_qname, local_name, get_extended_qname
from ..namespaces import NamespaceResourcesMap
from .exceptions import XMLSchemaNotBuiltError, XMLSchemaModelError, XMLSchemaModelDepthError, \
    XMLSchemaParseError
from .xsdbase import XsdValidator, XsdComponent
from .builtins import xsd_builtin_types_factory
from . import XsdAttribute, XsdSimpleType, XsdComplexType, XsdElement, XsdAttributeGroup, \
    XsdGroup, XsdNotation, XsdIdentity, XsdAssert, XsdUnion, XsdAtomicRestriction
            raise XMLSchemaKeyError(f'xs:notation {qname!r} not found')
        else:
            if isinstance(obj, XsdNotation):
                return obj
            return cast(XsdNotation, self._build_global(obj, qname, self.notations))

    def lookup_type(self, qname: str) -> BaseXsdType:
        try:
            obj = self.types[qname]
        except KeyError:
            raise XMLSchemaKeyError(f'global xs:simpleType/xs:complexType {qname!r} not found')
        else:
            if isinstance(obj, (XsdSimpleType, XsdComplexType)):
                return obj
            return cast(BaseXsdType, self._build_global(obj, qname, self.types))

    def lookup_attribute(self, qname: str) -> XsdAttribute:
        try:
            obj = self.attributes[qname]
        except KeyError:
            raise XMLSchemaKeyError(f'global xs:attribute {qname!r} not found')
        else:
            if isinstance(obj, XsdAttribute):
                return obj
            return cast(XsdAttribute, self._build_global(obj, qname, self.attributes))

    def lookup_attribute_group(self, qname: str) -> XsdAttributeGroup:
        try:
            obj = self.attribute_groups[qname]
        except KeyError:
            raise XMLSchemaKeyError(f'global xs:attributeGroup {qname!r} not found')
        else:
            if isinstance(obj, XsdAttributeGroup):
                return obj
            return cast(XsdAttributeGroup, self._build_global(obj, qname, self.attribute_groups))

    def lookup_group(self, qname: str) -> XsdGroup:
        try:
            obj = self.groups[qname]
        except KeyError:
            raise XMLSchemaKeyError(f'global xs:group {qname!r} not found')
        else:
            if isinstance(obj, XsdGroup):
                return obj
            return cast(XsdGroup, self._build_global(obj, qname, self.groups))

    def lookup_element(self, qname: str) -> XsdElement:
        try:
            obj = self.elements[qname]
        except KeyError:
            raise XMLSchemaKeyError(f'global xs:element {qname!r} not found')
        else:
            if isinstance(obj, XsdElement):
                return obj
            return cast(XsdElement, self._build_global(obj, qname, self.elements))

    def _build_global(self, obj: Any, qname: str,
                      global_map: Dict[str, Any]) -> Any:
        factory_or_class: Callable[[ElementType, SchemaType], Any]

        if isinstance(obj, tuple):
            # Not built XSD global component without redefinitions
            try:
                elem, schema = obj
            except ValueError:
                return obj[0]  # Circular build, simply return (elem, schema) couple

            try:
                factory_or_class = self._builders[elem.tag]
            except KeyError:
                raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map))

            global_map[qname] = obj,  # Encapsulate into a tuple to catch circular builds
            global_map[qname] = factory_or_class(elem, schema)
            return global_map[qname]

        elif isinstance(obj, list):
            # Not built XSD global component with redefinitions
            try:
                elem, schema = obj[0]
            except ValueError:
                return obj[0][0]  # Circular build, simply return (elem, schema) couple

            try:
                factory_or_class = self._builders[elem.tag]
            except KeyError:
                raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map))

            global_map[qname] = obj[0],  # To catch circular builds
            global_map[qname] = component = factory_or_class(elem, schema)

            # Apply redefinitions (changing elem involve a re-parsing of the component)
            for elem, schema in obj[1:]:
                if component.schema.target_namespace != schema.target_namespace:
                    msg = "redefined schema {!r} has a different targetNamespace"
                    raise XMLSchemaValueError(msg.format(schema))

                component.redefine = component.copy()
                component.redefine.parent = component
                component.schema = schema
                component.elem = elem

            return global_map[qname]

        else:
            raise XMLSchemaTypeError(f"unexpected instance {obj} in global map")

    def get_instance_type(self, type_name: str, base_type: BaseXsdType,
                          namespaces: MutableMapping[str, str]) -> BaseXsdType:
        """
        Returns the instance XSI type from global maps, validating it with the reference base type.

        :param type_name: the XSI type attribute value, a QName in prefixed format.
        :param base_type: the XSD from which the instance type has to be derived.
        :param namespaces: a mapping from prefixes to namespaces.
        """
        if isinstance(base_type, XsdComplexType) and XSI_TYPE in base_type.attributes:
            xsd_attribute = cast(XsdAttribute, base_type.attributes[XSI_TYPE])
            xsd_attribute.validate(type_name)


        extended_name = get_extended_qname(type_name, namespaces)