Source code for xsdata.codegen.mappers.schema

from typing import Dict
from typing import Iterator
from typing import List
from typing import Optional
from typing import Tuple

from xsdata.codegen.models import Attr
from xsdata.codegen.models import AttrType
from xsdata.codegen.models import Class
from xsdata.codegen.models import Extension
from xsdata.codegen.models import Restrictions
from xsdata.models.enums import DataType
from xsdata.models.enums import Namespace
from xsdata.models.enums import Tag
from xsdata.models.mixins import ElementBase
from xsdata.models.xsd import Attribute
from xsdata.models.xsd import AttributeGroup
from xsdata.models.xsd import ComplexType
from xsdata.models.xsd import Element
from xsdata.models.xsd import Group
from xsdata.models.xsd import Schema
from xsdata.models.xsd import SimpleType
from xsdata.utils import text
from xsdata.utils.namespaces import build_qname


[docs]class SchemaMapper: """Map a schema instance to classes, extensions and attributes."""
[docs] @classmethod def map(cls, schema: Schema) -> List[Class]: """Map schema children elements to classes.""" return [ cls.build_class(element, container, schema.module, schema.target_namespace) for container, element in cls.root_elements(schema) ]
[docs] @classmethod def root_elements(cls, schema: Schema): """Return all valid schema elements that can be converted to classes.""" for override in schema.overrides: for child in override.children(condition=cls.is_class): yield Tag.OVERRIDE, child for redefine in schema.redefines: for child in redefine.children(condition=cls.is_class): yield Tag.REDEFINE, child for child in schema.children(condition=cls.is_class): yield Tag.SCHEMA, child
[docs] @classmethod def build_class( cls, obj: ElementBase, container: str, module: str, target_namespace: Optional[str], ) -> Class: """Build and return a class instance.""" instance = Class( qname=build_qname(target_namespace, obj.real_name), abstract=obj.is_abstract, namespace=cls.element_namespace(obj, target_namespace), mixed=obj.is_mixed, nillable=obj.is_nillable, type=type(obj), container=container, help=obj.display_help, ns_map=obj.ns_map, module=module, substitutions=cls.build_substitutions(obj, target_namespace), ) cls.build_class_extensions(obj, instance) cls.build_class_attributes(obj, instance) return instance
[docs] @classmethod def build_substitutions( cls, obj: ElementBase, target_namespace: Optional[str] ) -> List[str]: return [ build_qname(obj.ns_map.get(prefix, target_namespace), suffix) for prefix, suffix in map(text.split, obj.substitutions) ]
[docs] @classmethod def build_class_attributes(cls, obj: ElementBase, target: Class): """Build the target class attributes from the given ElementBase children.""" restrictions = Restrictions.from_element(obj) for child, restrictions in cls.element_children(obj, restrictions): cls.build_class_attribute(target, child, restrictions) target.attrs.sort(key=lambda x: x.index)
[docs] @classmethod def build_class_extensions(cls, obj: ElementBase, target: Class): """Build the item class extensions from the given ElementBase children.""" extensions = set() raw_type = obj.raw_type if raw_type: restrictions = obj.get_restrictions() extensions.add(cls.build_class_extension(target, raw_type, restrictions)) extensions.update(cls.children_extensions(obj, target)) target.extensions = list(extensions)
[docs] @classmethod def build_data_type( cls, target: Class, name: str, forward: bool = False ) -> AttrType: """Create an attribute type for the target class.""" prefix, suffix = text.split(name) namespace = target.ns_map.get(prefix, target.target_namespace) native = ( Namespace.get_enum(namespace) is not None and DataType.get_enum(suffix) is not None ) return AttrType( qname=build_qname(namespace, suffix), native=native, forward=forward, )
[docs] @classmethod def element_children( cls, obj: ElementBase, parent_restrictions: Restrictions ) -> Iterator[Tuple[ElementBase, Restrictions]]: """Recursively find and return all child elements that are qualified to be class attributes, with all their restrictions.""" for child in obj.children(): if child.is_attribute: yield child, parent_restrictions else: restrictions = parent_restrictions.clone() restrictions.merge(Restrictions.from_element(child)) yield from cls.element_children(child, restrictions)
[docs] @classmethod def element_namespace( cls, obj: ElementBase, target_namespace: Optional[str] ) -> Optional[str]: """ Return the target namespace for the given schema element. In order: - elements/attributes with specific target namespace - prefixed elements returns the namespace from schema ns_map - qualified elements returns the schema target namespace - unqualified elements return an empty string - unqualified attributes return None """ raw_namespace = obj.raw_namespace if raw_namespace: return raw_namespace prefix = obj.prefix if prefix: return obj.ns_map.get(prefix) if obj.is_qualified: return target_namespace return "" if isinstance(obj, Element) else None
[docs] @classmethod def children_extensions( cls, obj: ElementBase, target: Class ) -> Iterator[Extension]: """ Recursively find and return all target's Extension classes. If the initial given obj has a type attribute include it in result. """ for child in obj.children(): if child.is_attribute: continue for ext in child.extensions: yield cls.build_class_extension(target, ext, child.get_restrictions()) yield from cls.children_extensions(child, target)
[docs] @classmethod def build_class_extension( cls, target: Class, name: str, restrictions: Dict ) -> Extension: """Create an extension for the target class.""" return Extension( type=cls.build_data_type(target, name), restrictions=Restrictions(**restrictions), )
[docs] @classmethod def build_class_attribute( cls, target: Class, obj: ElementBase, parent_restrictions: Restrictions ): """Generate and append an attribute field to the target class.""" target.ns_map.update(obj.ns_map) types = cls.build_class_attribute_types(target, obj) restrictions = Restrictions.from_element(obj) if obj.class_name in (Tag.ELEMENT, Tag.ANY, Tag.GROUP): restrictions.merge(parent_restrictions) if restrictions.prohibited: return name = obj.real_name target.attrs.append( Attr( index=obj.index, name=name, local_name=name, default=obj.default_value, fixed=obj.is_fixed, types=types, tag=obj.class_name, help=obj.display_help, namespace=cls.element_namespace(obj, target.target_namespace), restrictions=restrictions, ) )
[docs] @classmethod def build_class_attribute_types( cls, target: Class, obj: ElementBase ) -> List[AttrType]: """Convert real type and anonymous inner types to an attribute type list.""" types = [cls.build_data_type(target, name) for name in obj.real_type.split()] for inner in cls.build_inner_classes( obj, target.module, target.target_namespace ): target.inner.append(inner) types.append(cls.build_data_type(target, name=inner.name, forward=True)) if len(types) == 0: types.append(cls.build_data_type(target, name=obj.default_type)) return types
[docs] @classmethod def build_inner_classes( cls, obj: ElementBase, module: str, namespace: Optional[str] ) -> Iterator[Class]: """Find and convert anonymous types to a class instances.""" if isinstance(obj, SimpleType) and obj.is_enumeration: yield cls.build_class(obj, obj.class_name, module, namespace) else: for child in obj.children(): if isinstance(child, ComplexType) or ( isinstance(child, SimpleType) and child.is_enumeration ): child.name = obj.real_name yield cls.build_class(child, obj.class_name, module, namespace) else: yield from cls.build_inner_classes(child, module, namespace)
[docs] @classmethod def is_class(cls, item: ElementBase) -> bool: return isinstance( item, (SimpleType, ComplexType, Group, AttributeGroup, Element, Attribute) )