Exemple #1
0
 def reset_attribute_type(cls, attr_type: AttrType, use_str: bool = True):
     """Reset the attribute type to string or any simple type."""
     attr_type.qname = str(
         DataType.STRING if use_str else DataType.ANY_SIMPLE_TYPE)
     attr_type.native = True
     attr_type.circular = False
     attr_type.forward = False
    def process(cls, target: Class):
        """Add or update an existing an xs:anyType derived attribute if the
        target class supports mixed content."""

        if not target.mixed:
            return

        wildcard = first(attr for attr in target.attrs if attr.tag == Tag.ANY)

        if wildcard:
            wildcard.mixed = True
            if not wildcard.is_list:
                wildcard.restrictions.min_occurs = 0
                wildcard.restrictions.max_occurs = sys.maxsize
        else:
            attr = Attr(
                name="content",
                types=[AttrType(qname=str(DataType.ANY_TYPE), native=True)],
                tag=Tag.ANY,
                mixed=True,
                namespace=NamespaceType.ANY_NS,
                restrictions=Restrictions(min_occurs=0,
                                          max_occurs=sys.maxsize),
            )
            target.attrs.insert(0, attr)
Exemple #3
0
    def group_fields(self, target: Class, attrs: List[Attr]):
        """Group attributes into a new compound field."""

        pos = target.attrs.index(attrs[0])
        names = []
        choices = []
        min_occurs = []
        max_occurs = []
        for attr in attrs:
            target.attrs.remove(attr)
            names.append(attr.local_name)
            min_occurs.append(attr.restrictions.min_occurs)
            max_occurs.append(attr.restrictions.max_occurs)
            choices.append(self.build_attr_choice(attr))

        name = "choice" if len(names) > 3 else "_Or_".join(names)
        target.attrs.insert(
            pos,
            Attr(
                name=name,
                index=0,
                types=[AttrType(qname=str(DataType.ANY_TYPE), native=True)],
                tag=Tag.CHOICE,
                restrictions=Restrictions(
                    min_occurs=min((x for x in min_occurs if x is not None),
                                   default=0),
                    max_occurs=max((x for x in max_occurs if x is not None),
                                   default=0),
                ),
                choices=choices,
            ),
        )
Exemple #4
0
    def copy_inner_class(cls, source: Class, target: Class, attr: Attr,
                         attr_type: AttrType):
        """
        Check if the given attr type is a forward reference and copy its inner
        class from the source to the target class.

        Checks:
            1. Update type if inner class in a circular reference
            2. Copy inner class, rename it if source is a simple type.
        """
        if not attr_type.forward:
            return

        # This will fail if no inner class is found, too strict???
        inner = ClassUtils.find_inner(source, attr_type.qname)

        if inner is target:
            attr_type.circular = True
        else:
            clone = inner.clone()
            clone.package = target.package
            clone.module = target.module

            # Simple type, update the name
            if clone.name == "@value":
                namespace, _ = split_qname(clone.qname)
                clone.qname = attr_type.qname = build_qname(
                    namespace, attr.name)

            target.inner.append(clone)
Exemple #5
0
    def process(cls, target: Class):
        """Process classes that contain attributes derived from xs:enumeration
        and any other xs element."""

        if target.is_enumeration or not any(attr.is_enumeration
                                            for attr in target.attrs):
            return

        enumerations = []
        for attr in list(target.attrs):
            if attr.is_enumeration:
                target.attrs.remove(attr)
                enumerations.append(attr)

        if len(target.attrs) > 1:
            raise AnalyzerValueError(
                "Mixed enumeration with more than one normal field.")

        enum_inner = next(
            (inner for inner in target.inner if inner.is_enumeration), None)
        if not enum_inner:
            enum_inner = Class(
                name="value",
                type=SimpleType,
                module=target.module,
                package=target.package,
                mixed=False,
                abstract=False,
                nillable=False,
            )
            target.attrs[0].types.append(AttrType(name="value", forward=True))
            target.inner.append(enum_inner)

        enum_inner.attrs.extend(enumerations)
Exemple #6
0
    def process_dependency_type(self, target: Class, attr: Attr,
                                attr_type: AttrType):
        """
        Process an attributes type that depends on any global type.

        Strategies:
            1. Reset absent or dummy attribute types with a warning
            2. Copy attribute properties from a simple type
            3. Copy format restriction from an enumeration
            4. Set circular flag for the rest
        """

        source = self.find_dependency(attr_type, attr.tag)
        if not source or (not source.attrs and not source.extensions):
            logger.warning("Reset dummy type: %s", attr_type.name)
            use_str = not source or not source.is_complex
            self.reset_attribute_type(attr_type, use_str)
        elif source.is_simple_type:
            self.copy_attribute_properties(source, target, attr, attr_type)
        elif source.is_enumeration:
            attr.restrictions.format = collections.first(
                x.restrictions.format for x in source.attrs
                if x.restrictions.format)
            attr_type.reference = id(source)
        else:
            self.set_circular_flag(source, target, attr_type)
    def reset_unsupported_types(cls, target: Class):
        for attr in target.attrs:

            if not cls.validate_default_value(attr, target.ns_map):
                attr.types.clear()
                attr.types.append(AttrType(qname=str(DataType.STRING), native=True))
                attr.restrictions.format = None

            attr.types = collections.unique_sequence(attr.types, key="qname")
Exemple #8
0
    def build_class_attribute_types(
        self, target: Class, obj: ElementBase
    ) -> List[AttrType]:
        """Convert real type and anonymous inner types to an attribute type
        list."""
        types = [
            self.build_data_type(target, name)
            for name in (obj.real_type or "").split(" ")
            if name
        ]

        for inner in self.build_inner_classes(obj):
            target.inner.append(inner)
            types.append(AttrType(name=inner.name, forward=True))

        if len(types) == 0:
            types.append(AttrType(name=obj.default_type.code, native=True))

        return types
    def create_substitution(cls, source: Class) -> Attr:
        """Create an attribute with type that refers to the given source class
        and namespaced qualified name."""

        return Attr(
            name=source.name,
            types=[AttrType(qname=source.qname)],
            tag=source.type.__name__,
            namespace=source.namespace,
        )
    def test_create_substitution(self):
        item = ClassFactory.elements(1, qname=build_qname("foo", "bar"))
        actual = self.processor.create_substitution(item)

        expected = AttrFactory.create(
            name=item.name,
            default=None,
            types=[AttrType(qname=build_qname("foo", "bar"))],
            tag=item.type.__name__,
        )

        self.assertEqual(expected, actual)
Exemple #11
0
    def build_data_type(
        cls, target: Class, name: str, index: int = 0, forward: bool = False
    ) -> AttrType:
        """Create an attribute type for the target class."""
        prefix, suffix = text.split(name)
        native = False
        namespace = target.ns_map.get(prefix)

        if Namespace.get_enum(namespace) and DataType.get_enum(suffix):
            name = suffix
            native = True

        return AttrType(name=name, index=index, native=native, forward=forward,)
Exemple #12
0
    def build_data_type(
        cls, target: Class, name: str, index: int = 0, forward: bool = False
    ) -> AttrType:
        """Create an attribute type for the target class."""
        prefix, suffix = text.split(name)
        namespace = target.ns_map.get(prefix, target.qname.namespace)
        native = (
            Namespace.get_enum(namespace) is not None
            and DataType.get_enum(suffix) is not None
        )

        return AttrType(
            qname=QName(namespace, suffix), index=index, native=native, forward=forward,
        )
Exemple #13
0
    def build_class_attribute(cls, target: Class, name: str, value: Any):
        if isinstance(value, list):
            for val in value:
                cls.build_class_attribute(target, name, val)
                target.attrs[-1].restrictions.max_occurs = sys.maxsize
        else:
            if isinstance(value, dict):
                inner = cls.build_class(value, name)
                attr_type = AttrType(qname=inner.qname, forward=True)
                target.inner.append(inner)
            else:
                attr_type = ElementMapper.build_attribute_type(name, value)

            ElementMapper.build_attribute(target, name, attr_type)
Exemple #14
0
    def build_class(cls, element: AnyElement,
                    target_namespace: Optional[str]) -> Class:

        assert element.qname is not None

        namespace, name = split_qname(element.qname)
        target = Class(
            qname=build_qname(target_namespace, name),
            namespace=cls.select_namespace(namespace, target_namespace),
            tag=Tag.ELEMENT,
            module="",
        )
        children = [c for c in element.children if isinstance(c, AnyElement)]
        sequential_set = cls.sequential_names(children)

        for key, value in element.attributes.items():
            attr_type = cls.build_attribute_type(key, value)
            cls.build_attribute(target, key, attr_type, target_namespace,
                                Tag.ATTRIBUTE)

        for child in children:

            assert child.qname is not None

            if child.tail:
                target.mixed = True

            if child.attributes or child.children:
                inner = cls.build_class(child, target_namespace)
                attr_type = AttrType(qname=inner.qname, forward=True)
                target.inner.append(inner)
            else:
                attr_type = cls.build_attribute_type(child.qname, child.text)

            cls.build_attribute(
                target,
                child.qname,
                attr_type,
                target_namespace,
                Tag.ELEMENT,
                child.qname in sequential_set,
            )

        if element.text:
            attr_type = cls.build_attribute_type("value", element.text)
            cls.build_attribute(target, "value", attr_type, None,
                                Tag.SIMPLE_TYPE)

        return target
Exemple #15
0
    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)
        qname = build_qname(namespace, suffix)
        datatype = DataType.from_qname(qname)

        return AttrType(
            qname=qname,
            native=datatype is not None,
            forward=forward,
        )
Exemple #16
0
    def process(cls, target: Class):
        """Add an xs:anyType attribute to the given class if it supports mixed
        content and doesn't have a wildcard attribute yet."""

        if not target.mixed or target.has_wild_attr:
            return

        attr = Attr(
            name="content",
            local_name="content",
            index=0,
            types=[AttrType(name=DataType.ANY_TYPE.code, native=True)],
            tag=Tag.ANY,
            namespace=NamespaceType.ANY.value,
        )
        target.attrs.insert(0, attr)
Exemple #17
0
    def create_substitution(cls, source: Class, qname: QName) -> Attr:
        """Create an attribute with type that refers to the given source class
        and namespaced qualified name."""
        prefix = None
        if qname.namespace != source.source_namespace:
            prefix = source.source_prefix

        reference = f"{prefix}:{source.name}" if prefix else source.name
        return Attr(
            name=source.name,
            local_name=source.name,
            index=0,
            default=None,
            types=[AttrType(name=reference)],
            tag=source.type.__name__,
            namespace=source.namespace,
        )
Exemple #18
0
    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, tp) for tp in obj.attr_types]

        module = target.module
        namespace = target.target_namespace
        for inner in cls.build_inner_classes(obj, module, namespace):
            target.inner.append(inner)
            types.append(AttrType(qname=inner.qname, forward=True))

        if len(types) == 0:
            types.append(cls.build_data_type(target, name=obj.default_type))

        return collections.unique_sequence(types)
Exemple #19
0
 def build_attr(
     cls,
     name: str,
     qname: str,
     native: bool = False,
     forward: bool = False,
     namespace: Optional[str] = None,
     default: Optional[str] = None,
 ) -> Attr:
     """Builder method for attributes."""
     return Attr(
         tag=Tag.ELEMENT,
         name=name,
         namespace=namespace,
         default=default,
         types=[AttrType(qname=qname, forward=forward, native=native)],
     )
Exemple #20
0
    def build_attribute_type(cls, qname: str, value: Any) -> AttrType:
        def match_type(val: Any) -> DataType:
            if not isinstance(val, str):
                return DataType.from_value(val)

            for tp in converter.explicit_types():
                if converter.test(val, [tp]):
                    return DataType.from_type(tp)

            return DataType.STRING

        if qname == QNames.XSI_TYPE:
            data_type = DataType.QNAME
        elif value is None or value == "":
            data_type = DataType.ANY_SIMPLE_TYPE
        else:
            data_type = match_type(value)

        return AttrType(qname=str(data_type), native=True)
Exemple #21
0
 def build_attr(
     cls,
     name: str,
     qname: str,
     native: bool = False,
     forward: bool = False,
     namespace: Optional[str] = None,
     default: Optional[str] = None,
 ) -> Attr:
     """Builder method for attributes."""
     occurs = 1 if default is not None else None
     return Attr(
         tag=Tag.ELEMENT,
         name=name,
         namespace=namespace,
         default=default,
         types=[AttrType(qname=qname, forward=forward, native=native)],
         restrictions=Restrictions(min_occurs=occurs, max_occurs=occurs),
     )
Exemple #22
0
    def create(
        cls,
        qname: Optional[str] = None,
        alias: Optional[str] = None,
        native: bool = False,
        forward: bool = False,
        circular: bool = False,
        **kwargs: Any,
    ) -> AttrType:
        if not qname:
            qname = build_qname("xsdata", f"attr_{cls.next_letter()}")

        return AttrType(
            qname=str(qname),
            alias=alias,
            native=native,
            circular=circular,
            forward=forward,
        )
    def test_process(self):
        item = ClassFactory.create()
        self.processor.process(item)
        self.assertEqual(0, len(item.attrs))

        item = ClassFactory.elements(2, mixed=True)
        self.processor.process(item)
        expected = AttrFactory.create(
            name="content",
            index=0,
            types=[AttrType(name=DataType.ANY_TYPE.code, native=True)],
            tag=Tag.ANY,
            namespace="##any",
        )

        self.assertEqual(expected, item.attrs[0])
        self.assertEqual(3, len(item.attrs))

        self.processor.process(item)
        self.assertEqual(3, len(item.attrs))
    def test_create_substitution(self):
        item = ClassFactory.elements(1)
        actual = self.processor.create_substitution(item, QName("foo"))

        expected = AttrFactory.create(
            name=item.name,
            index=0,
            default=None,
            types=[AttrType(name=f"{item.source_prefix}:{item.name}")],
            tag=item.type.__name__,
        )

        self.assertEqual(expected, actual)

        actual = self.processor.create_substitution(item,
                                                    item.source_qname("foo"))
        self.assertEqual(item.name, actual.types[0].name)

        item.source_namespace = None
        actual = self.processor.create_substitution(item, QName("foo"))
        self.assertEqual(item.name, actual.types[0].name)
Exemple #25
0
    def group_fields(self, target: Class, attrs: List[Attr]):
        """Group attributes into a new compound field."""

        pos = target.attrs.index(attrs[0])
        choice = attrs[0].restrictions.choice
        sum_occurs = choice and choice.startswith("effective_")
        names = []
        choices = []
        min_occurs = []
        max_occurs = []
        for attr in attrs:
            target.attrs.remove(attr)
            names.append(attr.local_name)
            min_occurs.append(attr.restrictions.min_occurs or 0)
            max_occurs.append(attr.restrictions.max_occurs or 0)
            choices.append(self.build_attr_choice(attr))

        if len(names) > 3 or len(names) != len(set(names)):
            name = "choice"
        else:
            name = "_Or_".join(names)

        target.attrs.insert(
            pos,
            Attr(
                name=name,
                index=0,
                types=[AttrType(qname=str(DataType.ANY_TYPE), native=True)],
                tag=Tag.CHOICE,
                restrictions=Restrictions(
                    min_occurs=sum(min_occurs)
                    if sum_occurs else min(min_occurs),
                    max_occurs=sum(max_occurs)
                    if sum_occurs else max(max_occurs),
                ),
                choices=choices,
            ),
        )
Exemple #26
0
    def filter_types(cls, types: List[AttrType]) -> List[AttrType]:
        """
        Remove duplicate and invalid types.

        Invalid:
            1. xs:error
            2. xs:anyType and xs:anySimpleType when there are other types present
        """
        types = collections.unique_sequence(types, key="qname")
        types = collections.remove(types,
                                   lambda x: x.datatype == DataType.ERROR)

        if len(types) > 1:
            types = collections.remove(
                types,
                lambda x: x.datatype in
                (DataType.ANY_TYPE, DataType.ANY_SIMPLE_TYPE),
            )

        if not types:
            types.append(AttrType(qname=str(DataType.STRING), native=True))

        return types
Exemple #27
0
 def set_circular_flag(self, source: Class, target: Class,
                       attr_type: AttrType):
     """Update circular reference flag."""
     attr_type.circular = self.is_circular_dependency(source, target, set())
Exemple #28
0
 def reset_attribute_type(cls, attr_type: AttrType):
     """Reset the attribute type to native string."""
     attr_type.qname = QName(Namespace.XS.uri, DataType.STRING.code)
     attr_type.native = True
     attr_type.circular = False
     attr_type.forward = False
Exemple #29
0
 def reset_attribute_type(cls, attr_type: AttrType):
     """Reset the attribute type to native string."""
     attr_type.name = DataType.STRING.code
     attr_type.native = True
     attr_type.circular = False
     attr_type.forward = False