1#
2# Copyright (c), 2016-2020, SISSA (International School for Advanced Studies).
3# All rights reserved.
4# This file is distributed under the terms of the MIT License.
5# See the file 'LICENSE' in the root directory of the present
6# distribution, or http://opensource.org/licenses/MIT.
7#
8# @author Davide Brunato <brunato@sissa.it>
9#
10"""
11This module contains functions and classes for namespaces XSD declarations/definitions.
12"""
13import warnings
14from collections import Counter
15from functools import lru_cache
16from typing import cast, Any, Callable, Dict, List, Iterable, Iterator, \
17    MutableMapping, Optional, Set, Union, Tuple, Type
18
19from ..exceptions import XMLSchemaKeyError, XMLSchemaTypeError, XMLSchemaValueError, \
20    XMLSchemaRuntimeError, XMLSchemaWarning
21from ..names import XSD_NAMESPACE, XSD_REDEFINE, XSD_OVERRIDE, XSD_NOTATION, \
22    XSD_ANY_TYPE, XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_GROUP, \
23    XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ELEMENT, XSI_TYPE
24from ..aliases import ComponentClassType, ElementType, SchemaType, BaseXsdType, \
25    SchemaGlobalType
26from ..helpers import get_qname, local_name, get_extended_qname
27from ..namespaces import NamespaceResourcesMap
28from .exceptions import XMLSchemaNotBuiltError, XMLSchemaModelError, XMLSchemaModelDepthError, \
29    XMLSchemaParseError
30from .xsdbase import XsdValidator, XsdComponent
31from .builtins import xsd_builtin_types_factory
32from . import XsdAttribute, XsdSimpleType, XsdComplexType, XsdElement, XsdAttributeGroup, \
33    XsdGroup, XsdNotation, XsdIdentity, XsdAssert, XsdUnion, XsdAtomicRestriction
34
35
36#
37# Defines the load functions for XML Schema structures
38def create_load_function(tag: str) \
39        -> Callable[[Dict[str, Any], Iterable[SchemaType]], None]:
40
41    def load_xsd_globals(xsd_globals: Dict[str, Any],
42                         schemas: Iterable[SchemaType]) -> None:
43        redefinitions = []
44        for schema in schemas:
45            target_namespace = schema.target_namespace
46
47            for elem in schema.root:
48                if elem.tag not in {XSD_REDEFINE, XSD_OVERRIDE}:
49                    continue
50
51                location = elem.get('schemaLocation')
52                if location is None:
53                    continue
54                for child in filter(lambda x: x.tag == tag and 'name' in x.attrib, elem):
55                    qname = get_qname(target_namespace, child.attrib['name'])
56                    redefinitions.append((qname, elem, child, schema, schema.includes[location]))
57
58            for elem in filter(lambda x: x.tag == tag and 'name' in x.attrib, schema.root):
59                qname = get_qname(target_namespace, elem.attrib['name'])
60                if qname not in xsd_globals:
61                    xsd_globals[qname] = (elem, schema)
62                else:
63                    try:
64                        other_schema = xsd_globals[qname][1]
65                    except (TypeError, IndexError):
66                        pass
67                    else:
68                        # It's ignored or replaced in case of an override
69                        if other_schema.override is schema:
70                            continue
71                        elif schema.override is other_schema:
72                            xsd_globals[qname] = (elem, schema)
73                            continue
74
75                    msg = "global {} with name={!r} is already defined"
76                    schema.parse_error(msg.format(local_name(tag), qname))
77
78        redefined_names = Counter(x[0] for x in redefinitions)
79        for qname, elem, child, schema, redefined_schema in reversed(redefinitions):
80
81            # Checks multiple redefinitions
82            if redefined_names[qname] > 1:
83                redefined_names[qname] = 1
84
85                redefined_schemas: Any
86                redefined_schemas = [x[-1] for x in redefinitions if x[0] == qname]
87                if any(redefined_schemas.count(x) > 1 for x in redefined_schemas):
88                    msg = "multiple redefinition for {} {!r}"
89                    schema.parse_error(msg.format(local_name(child.tag), qname), child)
90                else:
91                    redefined_schemas = {x[-1]: x[-2] for x in redefinitions if x[0] == qname}
92                    for rs, s in redefined_schemas.items():
93                        while True:
94                            try:
95                                s = redefined_schemas[s]
96                            except KeyError:
97                                break
98
99                            if s is rs:
100                                msg = "circular redefinition for {} {!r}"
101                                schema.parse_error(msg.format(local_name(child.tag), qname), child)
102                                break
103
104            if elem.tag == XSD_OVERRIDE:
105                # Components which match nothing in the target schema are ignored. See the
106                # period starting with "Source declarations not present in the target set"
107                # of the paragraph https://www.w3.org/TR/xmlschema11-1/#override-schema.
108                if qname in xsd_globals:
109                    xsd_globals[qname] = (child, schema)
110            else:
111                # Append to a list if it's a redefine
112                try:
113                    xsd_globals[qname].append((child, schema))
114                except KeyError:
115                    schema.parse_error("not a redefinition!", child)
116                except AttributeError:
117                    xsd_globals[qname] = [xsd_globals[qname], (child, schema)]
118
119    return load_xsd_globals
120
121
122load_xsd_simple_types = create_load_function(XSD_SIMPLE_TYPE)
123load_xsd_attributes = create_load_function(XSD_ATTRIBUTE)
124load_xsd_attribute_groups = create_load_function(XSD_ATTRIBUTE_GROUP)
125load_xsd_complex_types = create_load_function(XSD_COMPLEX_TYPE)
126load_xsd_elements = create_load_function(XSD_ELEMENT)
127load_xsd_groups = create_load_function(XSD_GROUP)
128load_xsd_notations = create_load_function(XSD_NOTATION)
129
130
131class XsdGlobals(XsdValidator):
132    """
133    Mediator class for related XML schema instances. It stores the global
134    declarations defined in the registered schemas. Register a schema to
135    add its declarations to the global maps.
136
137    :param validator: the origin schema class/instance used for creating the global maps.
138    :param validation: the XSD validation mode to use, can be 'strict', 'lax' or 'skip'.
139    """
140    types: Dict[str, Union[BaseXsdType, Tuple[ElementType, SchemaType]]]
141    attributes: Dict[str, Union[XsdAttribute, Tuple[ElementType, SchemaType]]]
142    attribute_groups: Dict[str, Union[XsdAttributeGroup, Tuple[ElementType, SchemaType]]]
143    groups: Dict[str, Union[XsdGroup, Tuple[ElementType, SchemaType]]]
144    notations: Dict[str, Union[XsdNotation, Tuple[ElementType, SchemaType]]]
145    elements: Dict[str, Union[XsdElement, Tuple[ElementType, SchemaType]]]
146
147    substitution_groups: Dict[str, Set[XsdElement]]
148    identities: Dict[str, XsdIdentity]
149    global_maps: Tuple[Dict[str, Any], ...]
150
151    missing_locations: List[str]
152
153    _lookup_function_resolver = {
154        XSD_SIMPLE_TYPE: 'lookup_type',
155        XSD_COMPLEX_TYPE: 'lookup_type',
156        XSD_ELEMENT: 'lookup_element',
157        XSD_GROUP: 'lookup_group',
158        XSD_ATTRIBUTE: 'lookup_attribute',
159        XSD_ATTRIBUTE_GROUP: 'lookup_attribute_group',
160        XSD_NOTATION: 'lookup_notation',
161    }
162
163    def __init__(self, validator: SchemaType, validation: str = 'strict') -> None:
164        super(XsdGlobals, self).__init__(validation)
165
166        self.validator = validator
167        self.namespaces = NamespaceResourcesMap()  # Registered schemas by namespace URI
168        self.missing_locations = []     # Missing or failing resource locations
169
170        self.types = {}                 # Global types (both complex and simple)
171        self.attributes = {}            # Global attributes
172        self.attribute_groups = {}      # Attribute groups
173        self.groups = {}                # Model groups
174        self.notations = {}             # Notations
175        self.elements = {}              # Global elements
176        self.substitution_groups = {}   # Substitution groups
177        self.identities = {}            # Identity constraints (uniqueness, keys, keyref)
178
179        self.global_maps = (self.notations, self.types, self.attributes,
180                            self.attribute_groups, self.groups, self.elements)
181
182        self._builders: Dict[str, Callable[[ElementType, SchemaType], Any]] = {
183            XSD_NOTATION: validator.xsd_notation_class,
184            XSD_SIMPLE_TYPE: validator.simple_type_factory,
185            XSD_COMPLEX_TYPE: validator.xsd_complex_type_class,
186            XSD_ATTRIBUTE: validator.xsd_attribute_class,
187            XSD_ATTRIBUTE_GROUP: validator.xsd_attribute_group_class,
188            XSD_GROUP: validator.xsd_group_class,
189            XSD_ELEMENT: validator.xsd_element_class,
190        }
191
192    def __repr__(self) -> str:
193        return '%s(validator=%r, validation=%r)' % (
194            self.__class__.__name__, self.validator, self.validation
195        )
196
197    def copy(self, validator: Optional[SchemaType] = None,
198             validation: Optional[str] = None) -> 'XsdGlobals':
199        """Makes a copy of the object."""
200        obj = self.__class__(self.validator if validator is None else validator,
201                             validation or self.validation)
202
203        obj.namespaces.update(self.namespaces)
204        obj.types.update(self.types)
205        obj.attributes.update(self.attributes)
206        obj.attribute_groups.update(self.attribute_groups)
207        obj.groups.update(self.groups)
208        obj.notations.update(self.notations)
209        obj.elements.update(self.elements)
210        obj.substitution_groups.update(self.substitution_groups)
211        obj.identities.update(self.identities)
212        return obj
213
214    __copy__ = copy
215
216    def lookup(self, tag: str, qname: str) -> SchemaGlobalType:
217        """
218        General lookup method for XSD global components.
219
220        :param tag: the expanded QName of the XSD the global declaration/definition \
221        (eg. '{http://www.w3.org/2001/XMLSchema}element'), that is used to select \
222        the global map for lookup.
223        :param qname: the expanded QName of the component to be looked-up.
224        :returns: an XSD global component.
225        :raises: an XMLSchemaValueError if the *tag* argument is not appropriate for a global \
226        component, an XMLSchemaKeyError if the *qname* argument is not found in the global map.
227        """
228        lookup_function: Callable[[str], SchemaGlobalType]
229        try:
230            lookup_function = getattr(self, self._lookup_function_resolver[tag])
231        except KeyError:
232            msg = "wrong tag {!r} for an XSD global definition/declaration"
233            raise XMLSchemaValueError(msg.format(tag)) from None
234        else:
235            return lookup_function(qname)
236
237    def lookup_notation(self, qname: str) -> XsdNotation:
238        try:
239            obj = self.notations[qname]
240        except KeyError:
241            raise XMLSchemaKeyError(f'xs:notation {qname!r} not found')
242        else:
243            if isinstance(obj, XsdNotation):
244                return obj
245            return cast(XsdNotation, self._build_global(obj, qname, self.notations))
246
247    def lookup_type(self, qname: str) -> BaseXsdType:
248        try:
249            obj = self.types[qname]
250        except KeyError:
251            raise XMLSchemaKeyError(f'global xs:simpleType/xs:complexType {qname!r} not found')
252        else:
253            if isinstance(obj, (XsdSimpleType, XsdComplexType)):
254                return obj
255            return cast(BaseXsdType, self._build_global(obj, qname, self.types))
256
257    def lookup_attribute(self, qname: str) -> XsdAttribute:
258        try:
259            obj = self.attributes[qname]
260        except KeyError:
261            raise XMLSchemaKeyError(f'global xs:attribute {qname!r} not found')
262        else:
263            if isinstance(obj, XsdAttribute):
264                return obj
265            return cast(XsdAttribute, self._build_global(obj, qname, self.attributes))
266
267    def lookup_attribute_group(self, qname: str) -> XsdAttributeGroup:
268        try:
269            obj = self.attribute_groups[qname]
270        except KeyError:
271            raise XMLSchemaKeyError(f'global xs:attributeGroup {qname!r} not found')
272        else:
273            if isinstance(obj, XsdAttributeGroup):
274                return obj
275            return cast(XsdAttributeGroup, self._build_global(obj, qname, self.attribute_groups))
276
277    def lookup_group(self, qname: str) -> XsdGroup:
278        try:
279            obj = self.groups[qname]
280        except KeyError:
281            raise XMLSchemaKeyError(f'global xs:group {qname!r} not found')
282        else:
283            if isinstance(obj, XsdGroup):
284                return obj
285            return cast(XsdGroup, self._build_global(obj, qname, self.groups))
286
287    def lookup_element(self, qname: str) -> XsdElement:
288        try:
289            obj = self.elements[qname]
290        except KeyError:
291            raise XMLSchemaKeyError(f'global xs:element {qname!r} not found')
292        else:
293            if isinstance(obj, XsdElement):
294                return obj
295            return cast(XsdElement, self._build_global(obj, qname, self.elements))
296
297    def _build_global(self, obj: Any, qname: str,
298                      global_map: Dict[str, Any]) -> Any:
299        factory_or_class: Callable[[ElementType, SchemaType], Any]
300
301        if isinstance(obj, tuple):
302            # Not built XSD global component without redefinitions
303            try:
304                elem, schema = obj
305            except ValueError:
306                return obj[0]  # Circular build, simply return (elem, schema) couple
307
308            try:
309                factory_or_class = self._builders[elem.tag]
310            except KeyError:
311                raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map))
312
313            global_map[qname] = obj,  # Encapsulate into a tuple to catch circular builds
314            global_map[qname] = factory_or_class(elem, schema)
315            return global_map[qname]
316
317        elif isinstance(obj, list):
318            # Not built XSD global component with redefinitions
319            try:
320                elem, schema = obj[0]
321            except ValueError:
322                return obj[0][0]  # Circular build, simply return (elem, schema) couple
323
324            try:
325                factory_or_class = self._builders[elem.tag]
326            except KeyError:
327                raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map))
328
329            global_map[qname] = obj[0],  # To catch circular builds
330            global_map[qname] = component = factory_or_class(elem, schema)
331
332            # Apply redefinitions (changing elem involve a re-parsing of the component)
333            for elem, schema in obj[1:]:
334                if component.schema.target_namespace != schema.target_namespace:
335                    msg = "redefined schema {!r} has a different targetNamespace"
336                    raise XMLSchemaValueError(msg.format(schema))
337
338                component.redefine = component.copy()
339                component.redefine.parent = component
340                component.schema = schema
341                component.elem = elem
342
343            return global_map[qname]
344
345        else:
346            raise XMLSchemaTypeError(f"unexpected instance {obj} in global map")
347
348    def get_instance_type(self, type_name: str, base_type: BaseXsdType,
349                          namespaces: MutableMapping[str, str]) -> BaseXsdType:
350        """
351        Returns the instance XSI type from global maps, validating it with the reference base type.
352
353        :param type_name: the XSI type attribute value, a QName in prefixed format.
354        :param base_type: the XSD from which the instance type has to be derived.
355        :param namespaces: a mapping from prefixes to namespaces.
356        """
357        if isinstance(base_type, XsdComplexType) and XSI_TYPE in base_type.attributes:
358            xsd_attribute = cast(XsdAttribute, base_type.attributes[XSI_TYPE])
359            xsd_attribute.validate(type_name)
360
361        extended_name = get_extended_qname(type_name, namespaces)
362        xsi_type = self.lookup_type(extended_name)
363        if xsi_type.is_derived(base_type):
364            return xsi_type
365        elif isinstance(base_type, XsdSimpleType) and \
366                base_type.is_union() and not base_type.facets:
367            # Can be valid only if the union doesn't have facets, see:
368            #   https://www.w3.org/Bugs/Public/show_bug.cgi?id=4065
369            if isinstance(base_type, XsdAtomicRestriction) and \
370                    isinstance(base_type.primitive_type, XsdUnion):
371                if xsi_type in base_type.primitive_type.member_types:
372                    return xsi_type
373            elif isinstance(base_type, XsdUnion):
374                if xsi_type in base_type.member_types:
375                    return xsi_type
376
377        raise XMLSchemaTypeError("%r cannot substitute %r" % (xsi_type, base_type))
378
379    @property
380    def built(self) -> bool:
381        return all(schema.built for schema in self.iter_schemas())
382
383    @property
384    def unbuilt(self) -> List[Union[XsdComponent, SchemaType]]:
385        """Property that returns a list with unbuilt components."""
386        return [c for s in self.iter_schemas() for c in s.iter_components()
387                if c is not s and not c.built]
388
389    @property
390    def validation_attempted(self) -> str:
391        if self.built:
392            return 'full'
393        elif any(schema.validation_attempted == 'partial' for schema in self.iter_schemas()):
394            return 'partial'
395        else:
396            return 'none'
397
398    @property
399    def validity(self) -> str:
400        if not self.namespaces:
401            return 'notKnown'
402        if all(schema.validity == 'valid' for schema in self.iter_schemas()):
403            return 'valid'
404        elif any(schema.validity == 'invalid' for schema in self.iter_schemas()):
405            return 'invalid'
406        else:
407            return 'notKnown'
408
409    @property
410    def xsd_version(self) -> str:
411        return self.validator.XSD_VERSION
412
413    @property
414    def all_errors(self) -> List[XMLSchemaParseError]:
415        errors = []
416        for schema in self.iter_schemas():
417            errors.extend(schema.all_errors)
418        return errors
419
420    def create_bindings(self, *bases: Type[Any], **attrs: Any) -> None:
421        """Creates data object bindings for the XSD elements of built schemas."""
422        for xsd_element in self.iter_components(xsd_classes=XsdElement):
423            assert isinstance(xsd_element, XsdElement)
424            if xsd_element.target_namespace != XSD_NAMESPACE:
425                xsd_element.get_binding(*bases, replace_existing=True, **attrs)
426
427    def clear_bindings(self) -> None:
428        for xsd_element in self.iter_components(xsd_classes=XsdElement):
429            assert isinstance(xsd_element, XsdElement)
430            xsd_element.binding = None
431
432    def iter_components(self, xsd_classes: ComponentClassType = None) \
433            -> Iterator[Union['XsdGlobals', XsdComponent]]:
434        """Creates an iterator for the XSD components of built schemas."""
435        if xsd_classes is None or isinstance(self, xsd_classes):
436            yield self
437        for xsd_global in self.iter_globals():
438            yield from xsd_global.iter_components(xsd_classes)
439
440    def iter_globals(self) -> Iterator[SchemaGlobalType]:
441        """Creates an iterator for the XSD global components of built schemas."""
442        for global_map in self.global_maps:
443            yield from global_map.values()
444
445    def iter_schemas(self) -> Iterator[SchemaType]:
446        """Creates an iterator for the registered schemas."""
447        for schemas in self.namespaces.values():
448            yield from schemas
449
450    def register(self, schema: SchemaType) -> None:
451        """Registers an XMLSchema instance."""
452        try:
453            ns_schemas = self.namespaces[schema.target_namespace]
454        except KeyError:
455            self.namespaces[schema.target_namespace] = [schema]
456        else:
457            if schema in ns_schemas:
458                return
459            elif schema.url is None:
460                # only by multi-source init or add_schema() by user initiative
461                ns_schemas.append(schema)
462            elif not any(schema.url == obj.url and schema.__class__ is obj.__class__
463                         for obj in ns_schemas):
464                ns_schemas.append(schema)
465
466    @lru_cache(maxsize=1000)
467    def load_namespace(self, namespace: str, build: bool = True) -> bool:
468        """
469        Load namespace from available location hints. Returns `True` if the namespace
470        is already loaded or if the namespace can be loaded from one of the locations,
471        returns `False` otherwise. Failing locations are inserted into the missing
472        locations list.
473
474        :param namespace: the namespace to load.
475        :param build: if left with `True` value builds the maps after load. If the \
476        build fails the resource URL is added to missing locations.
477        """
478        namespace = namespace.strip()
479        if namespace in self.namespaces:
480            return True
481        elif self.validator.meta_schema is None:
482            return False  # Do not load additional namespaces for meta-schema (XHTML)
483
484        # Try from schemas location hints: usually the namespaces related to these
485        # hints are already loaded during schema construction, but it's better to
486        # retry once if the initial load has failed.
487        for schema in self.iter_schemas():
488            for url in schema.get_locations(namespace):
489                if url in self.missing_locations:
490                    continue
491
492                try:
493                    if schema.import_schema(namespace, url, schema.base_url) is not None:
494                        if build:
495                            self.build()
496                except (OSError, IOError):
497                    pass
498                except XMLSchemaNotBuiltError:
499                    self.clear(remove_schemas=True, only_unbuilt=True)
500                    self.missing_locations.append(url)
501                else:
502                    return True
503
504        # Try from library location hint, if there is any.
505        if namespace in self.validator.fallback_locations:
506            url = self.validator.fallback_locations[namespace]
507            if url not in self.missing_locations:
508                try:
509                    if self.validator.import_schema(namespace, url) is not None:
510                        if build:
511                            self.build()
512                except (OSError, IOError):
513                    return False
514                except XMLSchemaNotBuiltError:
515                    self.clear(remove_schemas=True, only_unbuilt=True)
516                    self.missing_locations.append(url)
517                else:
518                    return True
519
520        return False
521
522    def clear(self, remove_schemas: bool = False, only_unbuilt: bool = False) -> None:
523        """
524        Clears the instance maps and schemas.
525
526        :param remove_schemas: removes also the schema instances.
527        :param only_unbuilt: removes only not built objects/schemas.
528        """
529        global_map: Dict[str, XsdComponent]
530        if only_unbuilt:
531            not_built_schemas = {s for s in self.iter_schemas() if not s.built}
532            if not not_built_schemas:
533                return
534
535            for global_map in self.global_maps:
536                for k in list(global_map.keys()):
537                    obj = global_map[k]
538                    if not isinstance(obj, XsdComponent) or obj.schema in not_built_schemas:
539                        del global_map[k]
540                        if k in self.substitution_groups:
541                            del self.substitution_groups[k]
542                        if k in self.identities:
543                            del self.identities[k]
544
545            if remove_schemas:
546                namespaces = NamespaceResourcesMap()
547                for uri, value in self.namespaces.items():
548                    for schema in value:
549                        if schema not in not_built_schemas:
550                            namespaces[uri] = schema
551                self.namespaces = namespaces
552
553        else:
554            del self.missing_locations[:]
555            for global_map in self.global_maps:
556                global_map.clear()
557            self.substitution_groups.clear()
558            self.identities.clear()
559
560            if remove_schemas:
561                self.namespaces.clear()
562
563    def build(self) -> None:
564        """
565        Build the maps of XSD global definitions/declarations. The global maps are
566        updated adding and building the globals of not built registered schemas.
567        """
568        try:
569            meta_schema = self.namespaces[XSD_NAMESPACE][0]
570        except KeyError:
571            if self.validator.meta_schema is None:
572                msg = "missing XSD namespace in meta-schema instance {!r}"
573                raise XMLSchemaValueError(msg.format(self.validator))
574            meta_schema = None
575
576        if meta_schema is None or meta_schema.meta_schema is not None:
577            # XSD namespace not imported or XSD namespace not managed by a meta-schema.
578            # Creates a new meta-schema instance from the XSD meta-schema source and
579            # replaces the default meta-schema instance in all registered schemas.
580            if self.validator.meta_schema is None:
581                msg = "missing default meta-schema instance {!r}"
582                raise XMLSchemaRuntimeError(msg.format(self.validator))
583
584            url = self.validator.meta_schema.url
585            base_schemas = {k: v for k, v in self.validator.meta_schema.BASE_SCHEMAS.items()
586                            if k not in self.namespaces}
587            meta_schema = self.validator.create_meta_schema(url, base_schemas, self)
588
589            for schema in self.iter_schemas():
590                if schema.meta_schema is not None:
591                    schema.meta_schema = meta_schema
592        else:
593            if not self.types and meta_schema.maps is not self:
594                for source_map, target_map in zip(meta_schema.maps.global_maps, self.global_maps):
595                    target_map.update(source_map)
596
597        not_built_schemas = [schema for schema in self.iter_schemas() if not schema.built]
598        for schema in not_built_schemas:
599            schema._root_elements = None
600
601        # Load and build global declarations
602        load_xsd_simple_types(self.types, not_built_schemas)
603        load_xsd_complex_types(self.types, not_built_schemas)
604        load_xsd_notations(self.notations, not_built_schemas)
605        load_xsd_attributes(self.attributes, not_built_schemas)
606        load_xsd_attribute_groups(self.attribute_groups, not_built_schemas)
607        load_xsd_elements(self.elements, not_built_schemas)
608        load_xsd_groups(self.groups, not_built_schemas)
609
610        if not meta_schema.built:
611            xsd_builtin_types_factory(meta_schema, self.types)
612
613        if self is not meta_schema.maps:
614            # Rebuild xs:anyType for maps not owned by the meta-schema
615            # in order to do a correct namespace lookup for wildcards.
616            self.types[XSD_ANY_TYPE] = self.validator.create_any_type()
617
618        for qname in self.notations:
619            self.lookup_notation(qname)
620        for qname in self.attributes:
621            self.lookup_attribute(qname)
622
623        for qname in self.attribute_groups:
624            self.lookup_attribute_group(qname)
625        for schema in not_built_schemas:
626            if not isinstance(schema.default_attributes, str):
627                continue
628
629            try:
630                attributes = schema.maps.attribute_groups[schema.default_attributes]
631            except KeyError:
632                schema.default_attributes = None
633                msg = "defaultAttributes={!r} doesn't match an attribute group of {!r}"
634                schema.parse_error(
635                    error=msg.format(schema.root.get('defaultAttributes'), schema),
636                    elem=schema.root,
637                    validation=schema.validation
638                )
639            else:
640                schema.default_attributes = cast(XsdAttributeGroup, attributes)
641
642        for qname in self.types:
643            self.lookup_type(qname)
644        for qname in self.elements:
645            self.lookup_element(qname)
646        for qname in self.groups:
647            self.lookup_group(qname)
648
649        # Build element declarations inside model groups.
650        for schema in not_built_schemas:
651            for group in schema.iter_components(XsdGroup):
652                group.build()
653
654        # Build identity references and XSD 1.1 assertions
655        for schema in not_built_schemas:
656            for obj in schema.iter_components((XsdIdentity, XsdAssert)):
657                obj.build()
658
659        self.check(filter(lambda x: x.meta_schema is not None, not_built_schemas), self.validation)
660
661    def check(self, schemas: Optional[Iterable[SchemaType]] = None,
662              validation: str = 'strict') -> None:
663        """
664        Checks the global maps. For default checks all schemas and raises an
665        exception at first error.
666
667        :param schemas: optional argument with the set of the schemas to check.
668        :param validation: overrides the default validation mode of the validator.
669        :raise: XMLSchemaParseError
670        """
671        _schemas = set(schemas if schemas is not None else self.iter_schemas())
672
673        # Checks substitution groups circularity
674        for qname in self.substitution_groups:
675            xsd_element = self.elements[qname]
676            assert isinstance(xsd_element, XsdElement), "global element not built!"
677            if any(e is xsd_element for e in xsd_element.iter_substitutes()):
678                msg = "circularity found for substitution group with head element %r"
679                xsd_element.parse_error(msg.format(xsd_element), validation=validation)
680
681        if validation == 'strict' and not self.built:
682            raise XMLSchemaNotBuiltError(
683                self, "global map has unbuilt components: %r" % self.unbuilt
684            )
685
686        # Check redefined global groups restrictions
687        for group in self.groups.values():
688            assert isinstance(group, XsdGroup), "global group not built!"
689            if group.schema not in _schemas or group.redefine is None:
690                continue
691
692            while group.redefine is not None:
693                if not any(isinstance(e, XsdGroup) and e.name == group.name for e in group) \
694                        and not group.is_restriction(group.redefine):
695                    msg = "the redefined group is an illegal restriction of the original group"
696                    group.parse_error(msg, validation=validation)
697
698                group = group.redefine
699
700        # Check complex content types models restrictions
701        for xsd_global in filter(lambda x: x.schema in _schemas, self.iter_globals()):
702            xsd_type: Any
703            for xsd_type in xsd_global.iter_components(XsdComplexType):
704                if not isinstance(xsd_type.content, XsdGroup):
705                    continue
706
707                if xsd_type.derivation == 'restriction':
708                    base_type = xsd_type.base_type
709                    if base_type and base_type.name != XSD_ANY_TYPE and base_type.is_complex():
710                        if not xsd_type.content.is_restriction(base_type.content):
711                            xsd_type.parse_error("the derived group is an illegal restriction "
712                                                 "of the base type group", validation=validation)
713
714                    if base_type.is_complex() and not base_type.open_content and \
715                            xsd_type.open_content and xsd_type.open_content.mode != 'none':
716                        _group = xsd_type.schema.create_any_content_group(
717                            parent=xsd_type,
718                            any_element=xsd_type.open_content.any_element
719                        )
720                        if not _group.is_restriction(base_type.content):
721                            msg = "restriction has an open content but base type has not"
722                            _group.parse_error(msg, validation=validation)
723
724                try:
725                    xsd_type.content.check_model()
726                except XMLSchemaModelDepthError:
727                    msg = "cannot verify the content model of {!r} " \
728                          "due to maximum recursion depth exceeded".format(xsd_type)
729                    xsd_type.schema.warnings.append(msg)
730                    warnings.warn(msg, XMLSchemaWarning, stacklevel=4)
731                except XMLSchemaModelError as err:
732                    if validation == 'strict':
733                        raise
734                    xsd_type.errors.append(err)
735