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"""
11Observers for testing XMLSchema classes.
12"""
13from functools import wraps
14from typing import List
15
16from xmlschema.names import XSD_NAMESPACE, XSD_ANY_TYPE
17from xmlschema.validators import XMLSchema10, XMLSchema11, \
18    XsdGroup, XsdAttributeGroup, XsdComplexType, XsdComponent
19
20
21class SchemaObserver:
22    """
23    Observer that registers created components. Run the 'clear' method after each usage.
24    """
25    components: List[XsdComponent] = []
26    dummy_components: List[XsdComponent] = []
27
28    @classmethod
29    def observed_builder(cls, builder):
30        if isinstance(builder, type):
31            class BuilderProxy(builder):
32                def __init__(self, *args, **kwargs):
33                    super(BuilderProxy, self).__init__(*args, **kwargs)
34                    if not cls.is_dummy_component(self):
35                        cls.components.append(self)
36                    else:
37                        cls.dummy_components.append(self)
38            BuilderProxy.__name__ = builder.__name__
39            return BuilderProxy
40
41        elif callable(builder):
42            @wraps(builder)
43            def builder_proxy(*args, **kwargs):
44                obj = builder(*args, **kwargs)
45                if not cls.is_dummy_component(obj):
46                    cls.components.append(obj)
47                else:
48                    cls.dummy_components.append(obj)
49                return obj
50            return builder_proxy
51
52    @classmethod
53    def clear(cls):
54        del cls.components[:]
55        del cls.dummy_components[:]
56
57    @classmethod
58    def is_dummy_component(cls, component):
59        # Dummy components are empty attribute groups and xs:anyType
60        # definitions not related to XSD namespace.
61        if component.parent in cls.dummy_components:
62            return True
63        elif isinstance(component, XsdAttributeGroup):
64            return not component
65        elif isinstance(component, XsdComplexType):
66            return component.name == XSD_ANY_TYPE and \
67                component.target_namespace != XSD_NAMESPACE
68        elif isinstance(component, XsdGroup) and component.parent is not None:
69            return component.parent.name == XSD_ANY_TYPE and \
70                component.target_namespace != XSD_NAMESPACE
71        return False
72
73
74observed_builder = SchemaObserver.observed_builder
75
76
77class ObservedXMLSchema10(XMLSchema10):
78    xsd_notation_class = observed_builder(XMLSchema10.xsd_notation_class)
79    xsd_complex_type_class = observed_builder(XMLSchema10.xsd_complex_type_class)
80    xsd_attribute_class = observed_builder(XMLSchema10.xsd_attribute_class)
81    xsd_any_attribute_class = observed_builder(XMLSchema10.xsd_any_attribute_class)
82    xsd_attribute_group_class = observed_builder(XMLSchema10.xsd_attribute_group_class)
83    xsd_group_class = observed_builder(XMLSchema10.xsd_group_class)
84    xsd_element_class = observed_builder(XMLSchema10.xsd_element_class)
85    xsd_any_class = observed_builder(XMLSchema10.xsd_any_class)
86    xsd_atomic_restriction_class = observed_builder(XMLSchema10.xsd_atomic_restriction_class)
87    xsd_list_class = observed_builder(XMLSchema10.xsd_list_class)
88    xsd_union_class = observed_builder(XMLSchema10.xsd_union_class)
89    xsd_key_class = observed_builder(XMLSchema10.xsd_key_class)
90    xsd_keyref_class = observed_builder(XMLSchema10.xsd_keyref_class)
91    xsd_unique_class = observed_builder(XMLSchema10.xsd_unique_class)
92
93
94class ObservedXMLSchema11(XMLSchema11):
95    xsd_notation_class = observed_builder(XMLSchema11.xsd_notation_class)
96    xsd_complex_type_class = observed_builder(XMLSchema11.xsd_complex_type_class)
97    xsd_attribute_class = observed_builder(XMLSchema11.xsd_attribute_class)
98    xsd_any_attribute_class = observed_builder(XMLSchema11.xsd_any_attribute_class)
99    xsd_attribute_group_class = observed_builder(XMLSchema11.xsd_attribute_group_class)
100    xsd_group_class = observed_builder(XMLSchema11.xsd_group_class)
101    xsd_element_class = observed_builder(XMLSchema11.xsd_element_class)
102    xsd_any_class = observed_builder(XMLSchema11.xsd_any_class)
103    xsd_atomic_restriction_class = observed_builder(XMLSchema11.xsd_atomic_restriction_class)
104    xsd_list_class = observed_builder(XMLSchema11.xsd_list_class)
105    xsd_union_class = observed_builder(XMLSchema11.xsd_union_class)
106    xsd_key_class = observed_builder(XMLSchema11.xsd_key_class)
107    xsd_keyref_class = observed_builder(XMLSchema11.xsd_keyref_class)
108    xsd_unique_class = observed_builder(XMLSchema11.xsd_unique_class)
109