# -*- coding: utf-8 -*- # # Copyright (c), 2016-2019, SISSA (International School for Advanced Studies). # All rights reserved. # This file is distributed under the terms of the MIT License. # See the file 'LICENSE' in the root directory of the present # distribution, or http://opensource.org/licenses/MIT. # # @author Davide Brunato # from __future__ import unicode_literals from ..exceptions import XMLSchemaValueError from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENCE, \ XSD_ALL, XSD_CHOICE, XSD_ANY_ATTRIBUTE, XSD_ATTRIBUTE, XSD_COMPLEX_CONTENT, \ XSD_RESTRICTION, XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, XSD_SIMPLE_CONTENT, \ XSD_ANY_SIMPLE_TYPE, XSD_OPEN_CONTENT, XSD_ASSERT, get_qname, local_name from ..helpers import get_xsd_derivation_attribute from .exceptions import XMLSchemaValidationError, XMLSchemaDecodeError from .xsdbase import XsdType, ValidationMixin from .assertions import XsdAssert from .attributes import XsdAttributeGroup from .simple_types import XsdSimpleType from .groups import XsdGroup from .wildcards import XsdOpenContent XSD_MODEL_GROUP_TAGS = {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE} class XsdComplexType(XsdType, ValidationMixin): """ Class for XSD 1.0 *complexType* definitions. :var attributes: the attribute group related with the type. :var content_type: the content type, that can be a model group or a simple type. :var mixed: if `True` the complex type has mixed content. .. Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))) """ abstract = False mixed = False assertions = () open_content = None _block = None _ADMITTED_TAGS = {XSD_COMPLEX_TYPE, XSD_RESTRICTION} _CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ANY_ATTRIBUTE} @staticmethod def normalize(text): return text.decode('utf-8') if isinstance(text, bytes) else text def __init__(self, elem, schema, parent, name=None, **kwargs): if kwargs: if 'content_type' in kwargs: self.content_type = kwargs['content_type'] 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) def __repr__(self): if self.name is not None: return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name) elif not hasattr(self, 'content_type') 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 __setattr__(self, name, value): if name == 'content_type': assert isinstance(value, (XsdSimpleType, XsdGroup)), \ "The attribute 'content_type' must be a XsdSimpleType or an XsdGroup instance." elif name == 'attributes': assert isinstance(value, XsdAttributeGroup), \ "The attribute 'attributes' must be an XsdAttributeGroup." super(XsdComplexType, self).__setattr__(name, value) def _parse(self): super(XsdComplexType, self)._parse() elem = self.elem if elem.tag == XSD_RESTRICTION: return # a local restriction is already parsed by the caller if self._parse_boolean_attribute('abstract'): self.abstract = True if 'block' in elem.attrib: try: self._block = get_xsd_derivation_attribute(elem, 'block', ('extension', 'restriction')) except ValueError as err: self.parse_error(err, elem) if 'final' in elem.attrib: try: self._final = get_xsd_derivation_attribute(elem, 'final', ('extension', 'restriction')) except ValueError as err: self.parse_error(err, elem) if self._parse_boolean_attribute('mixed'): 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(elem, strict=False) if content_elem is None or content_elem.tag in self._CONTENT_TAIL_TAGS: self.content_type = self.schema.create_empty_content_group(self) self._parse_content_tail(elem) elif content_elem.tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}: self.content_type = self.schema.BUILDERS.group_class(content_elem, self.schema, self) self._parse_content_tail(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 elem[-1]: k = 2 if content_elem is not elem[0] else 1 self.parse_error("unexpected tag %r after simpleContent declaration:" % elem[k].tag, elem) 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 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 base_type = self._parse_base_type(derivation_elem, complex_content=True) if base_type is not self: self.base_type = base_type elif self.redefine: self.base_type = self.redefine if derivation_elem.tag == XSD_RESTRICTION: self._parse_complex_content_restriction(derivation_elem, base_type) else: self._parse_complex_content_extension(derivation_elem, base_type) if content_elem is not elem[-1]: k = 2 if content_elem is not elem[0] else 1 self.parse_error("unexpected tag %r after complexContent declaration:" % elem[k].tag, elem) 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 elem[-1]: self.content_type = self.schema.create_empty_content_group(self) else: for index, child in enumerate(elem): if content_elem is not child: continue elif elem[index + 1].tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}: self.content_type = self.schema.BUILDERS.group_class(elem[index + 1], self.schema, self) else: self.content_type = self.schema.self.schema.create_empty_content_group(self) break self._parse_content_tail(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, elem) self.content_type = 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", elem) elif self.base_type is None or self.base_type.name != self.name: self.parse_error("wrong redefinition without self-reference", elem) def _parse_content_tail(self, elem, **kwargs): self.attributes = self.schema.BUILDERS.attribute_group_class(elem, self.schema, self, **kwargs) def _parse_derivation_elem(self, elem): derivation_elem = self._parse_child_component(elem) if getattr(derivation_elem, 'tag', None) not in (XSD_RESTRICTION, XSD_EXTENSION): self.parse_error("restriction or extension tag expected", derivation_elem) self.content_type = self.schema.create_any_content_group(self) self.attributes = self.schema.create_any_attribute_group(self) return derivation = local_name(derivation_elem.tag) if self.derivation is None: self.derivation = derivation elif self.redefine is None: raise XMLSchemaValueError("%r is expected to have a redefined/overridden component" % self) if self.base_type is not None and derivation in self.base_type.final: self.parse_error("%r derivation not allowed for %r." % (derivation, self)) return derivation_elem def _parse_base_type(self, elem, complex_content=False): 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.maps.types[XSD_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.maps.types[XSD_ANY_TYPE] else: return self.maps.types[XSD_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.maps.types[XSD_ANY_TYPE] elif complex_content and base_type.is_simple(): self.parse_error("a complexType ancestor required: %r" % base_type, elem) return self.maps.types[XSD_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, base_type): # 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_type = self.schema.create_any_content_group(self) self._parse_content_tail(elem) else: if base_type.has_simple_content(): self.content_type = self.schema.BUILDERS.restriction_class(elem, self.schema, self) if not self.content_type.is_derived(base_type.content_type, 'restriction'): self.parse_error("Content type is not a restriction of base content type", elem) elif base_type.mixed and base_type.is_emptiable(): self.content_type = self.schema.BUILDERS.restriction_class(elem, self.schema, self) else: self.parse_error("with simple content cannot restrict an empty or " "an element-only content type ", base_type.elem) self.content_type = 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, base_type): # 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_type = base_type self._parse_content_tail(elem) else: if base_type.has_simple_content(): self.content_type = base_type.content_type else: self.parse_error("base type %r has not simple content." % base_type, elem) self.content_type = 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, base_type): 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.maps.types[XSD_ANY_TYPE] # complexContent restriction: the base type must be a complexType with a complex content. for child in filter(lambda x: x.tag != XSD_ANNOTATION, 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_type = self.schema.BUILDERS.group_class(child, self.schema, self) if not base_type.content_type.admits_restriction(content_type.model): msg = "restriction of an xs:{} with more than one particle with xs:{} is forbidden" self.parse_error(msg.format(base_type.content_type.model, content_type.model)) break else: content_type = self.schema.create_empty_content_group(self, base_type.content_type.model) content_type.restriction = base_type.content_type if base_type.is_element_only() and content_type.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_type.is_empty(): self.parse_error( "derived an empty content from base type that has not empty content.", elem ) if not self.open_content: if self.schema.default_open_content: self.open_content = self.schema.default_open_content elif getattr(base_type, 'open_content', None): self.open_content = base_type.open_content if self.open_content and content_type 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_type = content_type self._parse_content_tail(elem, derivation='restriction', base_attributes=base_type.attributes) def _parse_complex_content_extension(self, elem, base_type): if 'extension' in base_type.final: self.parse_error("the base type is not derivable by extension") for group_elem in filter(lambda x: x.tag != XSD_ANNOTATION, elem): 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_type = self.schema.BUILDERS.group_class(group_elem, self.schema, self) elif base_type.is_simple() or base_type.has_simple_content(): self.content_type = self.schema.create_empty_content_group(self) else: self.content_type = self.schema.create_empty_content_group( parent=self, model=base_type.content_type.model ) elif base_type.mixed: # Empty mixed model extension self.content_type = self.schema.create_empty_content_group(self) self.content_type.append(self.schema.create_empty_content_group(self.content_type)) if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS: group = self.schema.BUILDERS.group_class(group_elem, self.schema, self.content_type) 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_type.append(group) self.content_type.elem.append(base_type.content_type.elem) self.content_type.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.BUILDERS.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_type.model == 'all' and group.model == 'sequence': self.parse_error("xs:sequence cannot extend xs:all") content_type = self.schema.create_empty_content_group(self) content_type.append(base_type.content_type) content_type.append(group) content_type.elem.append(base_type.content_type.elem) content_type.elem.append(group.elem) if base_type.content_type.model == 'all' and base_type.content_type 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_type = content_type elif not base_type.is_simple() and not base_type.has_simple_content(): self.content_type = self.schema.create_empty_content_group(self) self.content_type.append(base_type.content_type) self.content_type.elem.append(base_type.content_type.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) else: self.content_type = self.schema.create_empty_content_group(self) self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes) @property def block(self): return self.schema.block_default if self._block is None else self._block @property def built(self): return self.content_type.parent is not None or self.content_type.built @property def validation_attempted(self): return 'full' if self.built else self.content_type.validation_attempted @staticmethod def is_simple(): return False @staticmethod def is_complex(): return True def is_empty(self): if self.name == XSD_ANY_TYPE: return False return self.content_type.is_empty() def is_emptiable(self): return self.content_type.is_emptiable() def has_simple_content(self): try: return self.content_type.is_simple() except AttributeError: if self.content_type or self.content_type.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_mixed_content(self): try: return self.content_type.mixed except AttributeError: return False def is_element_only(self): if self.name == XSD_ANY_TYPE: return False try: return not self.content_type.mixed except AttributeError: return False def is_list(self): return self.has_simple_content() and self.content_type.is_list() def is_valid(self, source, use_defaults=True, namespaces=None): if hasattr(source, 'tag'): return super(XsdComplexType, self).is_valid(source, use_defaults, namespaces) elif isinstance(self.content_type, XsdSimpleType): return self.content_type.is_valid(source, use_defaults, namespaces) else: return self.mixed or self.base_type is not None and \ self.base_type.is_valid(source, use_defaults, namespaces) def is_derived(self, other, derivation=None): if derivation and derivation == self.derivation: derivation = None # derivation mode checked if self is other: return derivation is None 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 hasattr(other, 'member_types'): 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 self.content_type.is_derived(other, derivation) elif self.has_simple_content(): return self.content_type.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=None): if xsd_classes is None or isinstance(self, xsd_classes): yield self if self.attributes.parent is not None: for obj in self.attributes.iter_components(xsd_classes): yield obj if self.content_type.parent is not None: for obj in self.content_type.iter_components(xsd_classes): yield obj if getattr(self.base_type, 'parent', None) is not None: for obj in self.base_type.iter_components(xsd_classes): yield obj 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 @staticmethod def get_facet(*_args, **_kwargs): return None def admit_simple_restriction(self): if 'restriction' in self.final: return False else: return self.has_simple_content() or self.mixed and self.is_emptiable() def has_restriction(self): return self.derivation == 'restriction' def has_extension(self): return self.derivation == 'extension' def text_decode(self, text): if self.has_simple_content(): return self.content_type.decode(text, validation='skip') else: return text def decode(self, data, *args, **kwargs): if hasattr(data, 'attrib') or self.is_simple(): return super(XsdComplexType, self).decode(data, *args, **kwargs) elif self.has_simple_content(): return self.content_type.decode(data, *args, **kwargs) else: raise XMLSchemaDecodeError(self, data, "cannot decode %r data with %r" % (data, self)) def iter_decode(self, elem, validation='lax', **kwargs): """ Decode an Element instance. :param elem: 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 3-tuple (simple content, complex content, attributes) containing \ the decoded parts, eventually preceded by a sequence of validation or decoding errors. """ # XSD 1.1 assertions for assertion in self.assertions: for error in assertion(elem, **kwargs): yield self.validation_error(validation, error, **kwargs) for result in self.attributes.iter_decode(elem.attrib, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: attributes = result break else: attributes = None if self.has_simple_content(): if len(elem) and validation != 'skip': reason = "a simple content element can't has child elements." yield self.validation_error(validation, reason, elem, **kwargs) if elem.text is not None: text = elem.text or kwargs.pop('default', '') for result in self.content_type.iter_decode(text, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: yield result, None, attributes else: yield None, None, attributes else: for result in self.content_type.iter_decode(elem, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: yield None, result, attributes def iter_encode(self, element_data, validation='lax', **kwargs): """ Encode an element data instance. :param element_data: an ElementData instance with unencoded data. :param validation: the validation mode: can be 'lax', 'strict' or 'skip'. :param kwargs: keyword arguments for the encoding process. :return: yields a 3-tuple (text, content, attributes) containing the encoded parts, \ eventually preceded by a sequence of validation or decoding errors. """ for result in self.attributes.iter_encode(element_data.attributes, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: attributes = result break else: attributes = () if self.has_simple_content(): if element_data.text is None: yield None, element_data.content, attributes else: for result in self.content_type.iter_encode(element_data.text, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: yield result, element_data.content, attributes else: for result in self.content_type.iter_encode(element_data, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result elif result: yield result[0], result[1], attributes else: yield None, None, attributes class Xsd11ComplexType(XsdComplexType): """ Class for XSD 1.1 *complexType* definitions. .. Content: (annotation?, (simpleContent | complexContent | (openContent?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?), assert*))) """ default_attributes_apply = True _CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE_GROUP, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE, XSD_ASSERT} def _parse(self): super(Xsd11ComplexType, self)._parse() if self.base_type and self.base_type.base_type is self.any_simple_type and \ self.base_type.derivation == 'extension' and not self.attributes: # Derivation from xs:anySimpleType with missing variety. # See: http://www.w3.org/TR/xmlschema11-1/#Simple_Type_Definition_details msg = "the simple content of {!r} is not a valid simple type in XSD 1.1" self.parse_error(msg.format(self.base_type)) # Add open content to complex content type if isinstance(self.content_type, XsdGroup): open_content = self.open_content or self.schema.default_open_content if open_content is None: pass elif open_content.mode == 'interleave': self.content_type.interleave = self.content_type.suffix = open_content.any_element elif open_content.mode == 'suffix': self.content_type.suffix = open_content.any_element # Add inheritable attributes if hasattr(self.base_type, 'attributes'): for name, attr in self.base_type.attributes.items(): if attr.inheritable: if name not in self.attributes: self.attributes[name] = attr elif not self.attributes[name].inheritable: self.parse_error("attribute %r must be inheritable") if 'defaultAttributesApply' in self.elem.attrib: if self.elem.attrib['defaultAttributesApply'].strip() in {'false', '0'}: self.default_attributes_apply = False # Add default attributes if self.redefine is None: default_attributes = self.schema.default_attributes else: default_attributes = self.redefine.schema.default_attributes if default_attributes is None: pass elif self.default_attributes_apply and not self.is_override(): if self.redefine is None and any(k in self.attributes for k in default_attributes): self.parse_error("at least a default attribute is already declared in the complex type") self.attributes.update( (k, v) for k, v in default_attributes.items() if k not in self.attributes ) def _parse_complex_content_extension(self, elem, base_type): # Complex content extension with simple base is forbidden XSD 1.1. # For the detailed rule refer to XSD 1.1 documentation: # https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#sec-cos-ct-extends 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 if 'extension' in base_type.final: self.parse_error("the base type is not derivable by extension") # Parse openContent for group_elem in filter(lambda x: x.tag != XSD_ANNOTATION, elem): if group_elem.tag != XSD_OPEN_CONTENT: break self.open_content = XsdOpenContent(group_elem, self.schema, self) try: self.open_content.any_element.union(base_type.open_content.any_element) except AttributeError: pass else: group_elem = None if not self.open_content: if self.schema.default_open_content: self.open_content = self.schema.default_open_content elif getattr(base_type, 'open_content', None): self.open_content = base_type.open_content try: if self.open_content and not base_type.open_content.is_restriction(self.open_content): msg = "{!r} is not an extension of the base type {!r}" self.parse_error(msg.format(self.open_content, base_type.open_content)) except AttributeError: pass if not base_type.content_type: if not base_type.mixed: # Empty element-only model extension: don't create a nested sequence group. if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS: self.content_type = self.schema.BUILDERS.group_class(group_elem, self.schema, self) else: self.content_type = self.schema.create_empty_content_group( parent=self, model=base_type.content_type.model ) elif base_type.mixed: # Empty mixed model extension self.content_type = self.schema.create_empty_content_group(self) self.content_type.append(self.schema.create_empty_content_group(self.content_type)) if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS: group = self.schema.BUILDERS.group_class(group_elem, self.schema, self.content_type) 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) if group.model == 'all': self.parse_error("cannot extend an empty mixed content with an xs:all") else: group = self.schema.create_empty_content_group(self) self.content_type.append(group) self.content_type.elem.append(base_type.content_type.elem) self.content_type.elem.append(group.elem) elif group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS: group = self.schema.BUILDERS.group_class(group_elem, self.schema, self) if base_type.content_type.model != 'all': content_type = self.schema.create_empty_content_group(self) content_type.append(base_type.content_type) content_type.elem.append(base_type.content_type.elem) if group.model == 'all': msg = "xs:all cannot extend a not empty xs:%s" self.parse_error(msg % base_type.content_type.model) else: content_type.append(group) content_type.elem.append(group.elem) else: content_type = self.schema.create_empty_content_group(self, model='all') content_type.extend(base_type.content_type) content_type.elem.extend(base_type.content_type.elem) if not group: pass elif group.model != 'all': self.parse_error("cannot extend a not empty 'all' model group with a different model") elif base_type.content_type.min_occurs != group.min_occurs: self.parse_error("when extend an xs:all group minOccurs must be the same") elif base_type.mixed and not base_type.content_type: self.parse_error("cannot extend an xs:all group with mixed empty content") else: content_type.extend(group) content_type.elem.extend(group.elem) 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_type = content_type elif not base_type.is_simple() and not base_type.has_simple_content(): self.content_type = self.schema.create_empty_content_group(self) self.content_type.append(base_type.content_type) self.content_type.elem.append(base_type.content_type.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) else: self.content_type = self.schema.create_empty_content_group(self) self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes) def _parse_content_tail(self, elem, **kwargs): self.attributes = self.schema.BUILDERS.attribute_group_class(elem, self.schema, self, **kwargs) self.assertions = [XsdAssert(e, self.schema, self, self) for e in elem if e.tag == XSD_ASSERT] if getattr(self.base_type, 'assertions', None): self.assertions.extend(assertion for assertion in self.base_type.assertions)