Skip to content

# merge_attributes

## `xsdata.codegen.handlers.merge_attributes`

### `MergeAttributes`

Bases: `HandlerInterface`

Merge same type attr and their restrictions.

Source code in `xsdata/codegen/handlers/merge_attributes.py`

     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93

    class MergeAttributes(HandlerInterface):
        """Merge same type attr and their restrictions."""

        __slots__ = ()

        @classmethod
        def process(cls, target: Class):
            """Detect and process duplicate attributes.

            Cases:
                - Enumeration: remove duplicates
                - Otherwise: merge attrs and

            Args:
                target: The target class instance
            """
            if target.is_enumeration:
                cls.filter_duplicate_attrs(target)
            else:
                cls.merge_duplicate_attrs(target)

        @classmethod
        def filter_duplicate_attrs(cls, target: Class):
            """Removes duplicate attrs.

            Args:
                target: The target class instance
            """
            attrs = collections.unique_sequence(target.attrs, key="default")
            target.attrs = attrs

        @classmethod
        def merge_duplicate_attrs(cls, target: Class):
            """Find duplicate attrs and merge them.

            In order for two attrs to be considered duplicates,
            they must have the same name, namespace and tag.

            Args:
                target: The target class instance
            """
            result: list[Attr] = []
            for attr in target.attrs:
                pos = collections.find(result, attr)
                existing = result[pos] if pos > -1 else None

                if not existing:
                    result.append(attr)
                elif not (attr.is_attribute or attr.is_enumeration):
                    existing.help = existing.help or attr.help

                    e_res = existing.restrictions
                    a_res = attr.restrictions

                    min_occurs = e_res.min_occurs or 0
                    max_occurs = e_res.max_occurs or 1
                    attr_min_occurs = a_res.min_occurs or 0
                    attr_max_occurs = a_res.max_occurs or 1

                    e_res.min_occurs = min(min_occurs, attr_min_occurs)

                    # Determine if attrs are mutually exclusive (max) or additive (sum)
                    is_mutually_exclusive = (
                        e_res.choice is not None
                        and a_res.choice is not None
                        and existing.index != attr.index
                        and (
                            # Same choice, different branches
                            e_res.choice == a_res.choice
                            # Different sibling choices at same depth
                            or len(e_res.path) == len(a_res.path)
                        )
                    )

                    if is_mutually_exclusive:
                        e_res.max_occurs = max(max_occurs, attr_max_occurs)
                    else:
                        e_res.max_occurs = max_occurs + attr_max_occurs

                    if a_res.sequence is not None:
                        e_res.sequence = a_res.sequence

                    existing.fixed = False
                    existing.types.extend(attr.types)

            target.attrs = result
            ClassUtils.cleanup_class(target)

#### `process(target)` `classmethod`

Detect and process duplicate attributes.

Cases

- Enumeration: remove duplicates
- Otherwise: merge attrs and

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`target` | `Class` | The target class instance | required

Source code in `xsdata/codegen/handlers/merge_attributes.py`

    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    @classmethod
    def process(cls, target: Class):
        """Detect and process duplicate attributes.

        Cases:
            - Enumeration: remove duplicates
            - Otherwise: merge attrs and

        Args:
            target: The target class instance
        """
        if target.is_enumeration:
            cls.filter_duplicate_attrs(target)
        else:
            cls.merge_duplicate_attrs(target)

#### `filter_duplicate_attrs(target)` `classmethod`

Removes duplicate attrs.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`target` | `Class` | The target class instance | required

Source code in `xsdata/codegen/handlers/merge_attributes.py`

    28
    29
    30
    31
    32
    33
    34
    35
    36

    @classmethod
    def filter_duplicate_attrs(cls, target: Class):
        """Removes duplicate attrs.

        Args:
            target: The target class instance
        """
        attrs = collections.unique_sequence(target.attrs, key="default")
        target.attrs = attrs

#### `merge_duplicate_attrs(target)` `classmethod`

Find duplicate attrs and merge them.

In order for two attrs to be considered duplicates, they must have the same name, namespace and tag.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`target` | `Class` | The target class instance | required

Source code in `xsdata/codegen/handlers/merge_attributes.py`

    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93

    @classmethod
    def merge_duplicate_attrs(cls, target: Class):
        """Find duplicate attrs and merge them.

        In order for two attrs to be considered duplicates,
        they must have the same name, namespace and tag.

        Args:
            target: The target class instance
        """
        result: list[Attr] = []
        for attr in target.attrs:
            pos = collections.find(result, attr)
            existing = result[pos] if pos > -1 else None

            if not existing:
                result.append(attr)
            elif not (attr.is_attribute or attr.is_enumeration):
                existing.help = existing.help or attr.help

                e_res = existing.restrictions
                a_res = attr.restrictions

                min_occurs = e_res.min_occurs or 0
                max_occurs = e_res.max_occurs or 1
                attr_min_occurs = a_res.min_occurs or 0
                attr_max_occurs = a_res.max_occurs or 1

                e_res.min_occurs = min(min_occurs, attr_min_occurs)

                # Determine if attrs are mutually exclusive (max) or additive (sum)
                is_mutually_exclusive = (
                    e_res.choice is not None
                    and a_res.choice is not None
                    and existing.index != attr.index
                    and (
                        # Same choice, different branches
                        e_res.choice == a_res.choice
                        # Different sibling choices at same depth
                        or len(e_res.path) == len(a_res.path)
                    )
                )

                if is_mutually_exclusive:
                    e_res.max_occurs = max(max_occurs, attr_max_occurs)
                else:
                    e_res.max_occurs = max_occurs + attr_max_occurs

                if a_res.sequence is not None:
                    e_res.sequence = a_res.sequence

                existing.fixed = False
                existing.types.extend(attr.types)

        target.attrs = result
        ClassUtils.cleanup_class(target)