Skip to content

APIMapping

Source code in library_analyzer/processing/migration/_api_mapping.py
class APIMapping:
    threshold_of_similarity_between_mappings: float
    threshold_of_similarity_for_creation_of_mappings: float
    threshold_of_merging_mappings: float
    apiv1: API
    apiv2: API
    differ: AbstractDiffer

    def __init__(
        self,
        apiv1: API,
        apiv2: API,
        differ: AbstractDiffer,
        threshold_of_similarity_for_creation_of_mappings: float = 0.5,
        threshold_of_similarity_between_mappings: float = 0.05,
        threshold_of_merging_mappings: float = 0.3,
    ) -> None:
        self.apiv1 = apiv1
        self.apiv2 = apiv2
        self.differ = differ
        self.threshold_of_similarity_for_creation_of_mappings = (
            threshold_of_similarity_for_creation_of_mappings
        )
        self.threshold_of_similarity_between_mappings = (
            threshold_of_similarity_between_mappings
        )
        self.threshold_of_merging_mappings = threshold_of_merging_mappings

    def _get_mappings_for_api_elements(
        self,
        api_elementv1_list: List[API_ELEMENTS],
        api_elementv2_list: List[API_ELEMENTS],
        compute_similarity: Callable[[API_ELEMENTS, API_ELEMENTS], float],
    ) -> list[Mapping]:
        element_mappings: list[Mapping] = []
        for api_elementv1 in api_elementv1_list:
            mapping_for_api_elementv1: list[Mapping] = []
            for api_elementv2 in api_elementv2_list:
                similarity = compute_similarity(api_elementv1, api_elementv2)
                if similarity >= self.threshold_of_similarity_for_creation_of_mappings:
                    mapping_for_api_elementv1.append(
                        OneToOneMapping(similarity, api_elementv1, api_elementv2)
                    )
            mapping_for_api_elementv1.sort(key=Mapping.get_similarity, reverse=True)
            new_mapping = self._merge_similar_mappings(mapping_for_api_elementv1)
            if new_mapping is not None:
                self._merge_mappings_with_same_elements(new_mapping, element_mappings)
        return element_mappings

    def map_api(self) -> List[Mapping]:
        mappings: List[Mapping] = []
        related_mappings = self.differ.get_related_mappings()
        if related_mappings is not None:
            for mapping in related_mappings:
                new_mapping = None
                if isinstance(
                    mapping.get_apiv1_elements()[0], Attribute
                ) and isinstance(mapping.get_apiv2_elements()[0], Attribute):
                    new_mapping = self._get_mappings_for_api_elements(
                        [
                            element
                            for element in mapping.get_apiv1_elements()
                            if isinstance(element, Attribute)
                        ],
                        [
                            element
                            for element in mapping.get_apiv2_elements()
                            if isinstance(element, Attribute)
                        ],
                        self.differ.compute_attribute_similarity,
                    )
                    mappings.extend(new_mapping)
                elif isinstance(mapping.get_apiv1_elements()[0], Class) and isinstance(
                    mapping.get_apiv2_elements()[0], Class
                ):
                    new_mapping = self._get_mappings_for_api_elements(
                        [
                            element
                            for element in mapping.get_apiv1_elements()
                            if isinstance(element, Class)
                        ],
                        [
                            element
                            for element in mapping.get_apiv2_elements()
                            if isinstance(element, Class)
                        ],
                        self.differ.compute_class_similarity,
                    )
                    mappings.extend(new_mapping)
                elif isinstance(
                    mapping.get_apiv1_elements()[0], Function
                ) and isinstance(mapping.get_apiv2_elements()[0], Function):
                    new_mapping = self._get_mappings_for_api_elements(
                        [
                            element
                            for element in mapping.get_apiv1_elements()
                            if isinstance(element, Function)
                        ],
                        [
                            element
                            for element in mapping.get_apiv2_elements()
                            if isinstance(element, Function)
                        ],
                        self.differ.compute_function_similarity,
                    )
                    mappings.extend(new_mapping)
                elif isinstance(
                    mapping.get_apiv1_elements()[0], Parameter
                ) and isinstance(mapping.get_apiv2_elements()[0], Parameter):
                    new_mapping = self._get_mappings_for_api_elements(
                        [
                            element
                            for element in mapping.get_apiv1_elements()
                            if isinstance(element, Parameter)
                        ],
                        [
                            element
                            for element in mapping.get_apiv2_elements()
                            if isinstance(element, Parameter)
                        ],
                        self.differ.compute_parameter_similarity,
                    )
                    mappings.extend(new_mapping)
                elif isinstance(mapping.get_apiv1_elements()[0], Result) and isinstance(
                    mapping.get_apiv2_elements()[0], Result
                ):
                    new_mapping = self._get_mappings_for_api_elements(
                        [
                            element
                            for element in mapping.get_apiv1_elements()
                            if isinstance(element, Result)
                        ],
                        [
                            element
                            for element in mapping.get_apiv2_elements()
                            if isinstance(element, Result)
                        ],
                        self.differ.compute_result_similarity,
                    )
                    mappings.extend(new_mapping)
                if new_mapping is not None and len(new_mapping) > 0:
                    self.differ.notify_new_mapping(new_mapping)
        else:
            mappings.extend(
                self._get_mappings_for_api_elements(
                    list(self.apiv1.classes.values()),
                    list(self.apiv2.classes.values()),
                    self.differ.compute_class_similarity,
                )
            )
            mappings.extend(
                self._get_mappings_for_api_elements(
                    list(self.apiv1.functions.values()),
                    list(self.apiv2.functions.values()),
                    self.differ.compute_function_similarity,
                )
            )
            mappings.extend(
                self._get_mappings_for_api_elements(
                    list(self.apiv1.parameters().values()),
                    list(self.apiv2.parameters().values()),
                    self.differ.compute_parameter_similarity,
                )
            )

            mappings.extend(
                self._get_mappings_for_api_elements(
                    [
                        attribute
                        for class_ in self.apiv1.classes.values()
                        for attribute in class_.instance_attributes
                    ],
                    [
                        attribute
                        for class_ in self.apiv2.classes.values()
                        for attribute in class_.instance_attributes
                    ],
                    self.differ.compute_attribute_similarity,
                )
            )

            mappings.extend(
                self._get_mappings_for_api_elements(
                    [
                        result
                        for function in self.apiv1.functions.values()
                        for result in function.results
                    ],
                    [
                        result
                        for function in self.apiv2.functions.values()
                        for result in function.results
                    ],
                    self.differ.compute_result_similarity,
                )
            )
        mappings.extend(self.differ.get_additional_mappings())
        mappings.sort(key=Mapping.get_similarity, reverse=True)
        return mappings

    def _merge_similar_mappings(self, mappings: List[Mapping]) -> Optional[Mapping]:
        """
        Given a list of OneToOne(Many)Mappings which apiv1 element is the same, this method returns the best mapping
        from this apiv1 element to apiv2 elements by merging the first and second elements recursively,
        if the difference in similarity is smaller than THRESHOLD_OF_SIMILARITY_BETWEEN_MAPPINGS.

        :param mappings: mappings sorted by decreasing similarity, which apiv1 element is the same
        :return: the first element of the sorted list that could be a result of merged similar mappings
        """
        if len(mappings) == 0:
            return None
        if len(mappings) == 1:
            return mappings[0]
        while (len(mappings) > 1) and (
            (mappings[0].similarity - mappings[1].similarity)
            < self.threshold_of_similarity_between_mappings
        ):
            mappings[0] = merge_mappings(mappings[0], mappings[1])
            mappings.pop(1)
        return mappings[0]

    def _merge_mappings_with_same_elements(
        self, mapping_to_be_appended: Mapping, mappings: list[Mapping]
    ) -> None:
        """
        This method prevents that an element in a mapping appears multiple times in a list of mappings
        by merging the affected mappings and include the result in the list. If there is no such element,
        the mapping will be included without any merge.

        :param mapping_to_be_appended: the mapping that should be included in mappings
        :param mappings: the list, in which mapping_to_be_appended should be appended
        """
        duplicated: list[Mapping] = []
        for mapping in mappings:
            duplicated_element = False
            for element in mapping.get_apiv2_elements():
                for element_2 in mapping_to_be_appended.get_apiv2_elements():
                    if element == element_2:
                        duplicated_element = True
                        break
            if duplicated_element:
                duplicated.append(mapping)

        if len(duplicated) == 0:
            mappings.append(mapping_to_be_appended)
            return

        for conflicted_mapping in duplicated:
            mapping_to_be_appended = merge_mappings(
                mapping_to_be_appended, conflicted_mapping
            )
            mappings.remove(conflicted_mapping)

        mappings.append(mapping_to_be_appended)

apiv1 = apiv1 instance-attribute

apiv2 = apiv2 instance-attribute

differ = differ instance-attribute

threshold_of_merging_mappings = threshold_of_merging_mappings instance-attribute

threshold_of_similarity_between_mappings = threshold_of_similarity_between_mappings instance-attribute

threshold_of_similarity_for_creation_of_mappings = threshold_of_similarity_for_creation_of_mappings instance-attribute

__init__(apiv1, apiv2, differ, threshold_of_similarity_for_creation_of_mappings=0.5, threshold_of_similarity_between_mappings=0.05, threshold_of_merging_mappings=0.3)

Source code in library_analyzer/processing/migration/_api_mapping.py
def __init__(
    self,
    apiv1: API,
    apiv2: API,
    differ: AbstractDiffer,
    threshold_of_similarity_for_creation_of_mappings: float = 0.5,
    threshold_of_similarity_between_mappings: float = 0.05,
    threshold_of_merging_mappings: float = 0.3,
) -> None:
    self.apiv1 = apiv1
    self.apiv2 = apiv2
    self.differ = differ
    self.threshold_of_similarity_for_creation_of_mappings = (
        threshold_of_similarity_for_creation_of_mappings
    )
    self.threshold_of_similarity_between_mappings = (
        threshold_of_similarity_between_mappings
    )
    self.threshold_of_merging_mappings = threshold_of_merging_mappings

map_api()

Source code in library_analyzer/processing/migration/_api_mapping.py
def map_api(self) -> List[Mapping]:
    mappings: List[Mapping] = []
    related_mappings = self.differ.get_related_mappings()
    if related_mappings is not None:
        for mapping in related_mappings:
            new_mapping = None
            if isinstance(
                mapping.get_apiv1_elements()[0], Attribute
            ) and isinstance(mapping.get_apiv2_elements()[0], Attribute):
                new_mapping = self._get_mappings_for_api_elements(
                    [
                        element
                        for element in mapping.get_apiv1_elements()
                        if isinstance(element, Attribute)
                    ],
                    [
                        element
                        for element in mapping.get_apiv2_elements()
                        if isinstance(element, Attribute)
                    ],
                    self.differ.compute_attribute_similarity,
                )
                mappings.extend(new_mapping)
            elif isinstance(mapping.get_apiv1_elements()[0], Class) and isinstance(
                mapping.get_apiv2_elements()[0], Class
            ):
                new_mapping = self._get_mappings_for_api_elements(
                    [
                        element
                        for element in mapping.get_apiv1_elements()
                        if isinstance(element, Class)
                    ],
                    [
                        element
                        for element in mapping.get_apiv2_elements()
                        if isinstance(element, Class)
                    ],
                    self.differ.compute_class_similarity,
                )
                mappings.extend(new_mapping)
            elif isinstance(
                mapping.get_apiv1_elements()[0], Function
            ) and isinstance(mapping.get_apiv2_elements()[0], Function):
                new_mapping = self._get_mappings_for_api_elements(
                    [
                        element
                        for element in mapping.get_apiv1_elements()
                        if isinstance(element, Function)
                    ],
                    [
                        element
                        for element in mapping.get_apiv2_elements()
                        if isinstance(element, Function)
                    ],
                    self.differ.compute_function_similarity,
                )
                mappings.extend(new_mapping)
            elif isinstance(
                mapping.get_apiv1_elements()[0], Parameter
            ) and isinstance(mapping.get_apiv2_elements()[0], Parameter):
                new_mapping = self._get_mappings_for_api_elements(
                    [
                        element
                        for element in mapping.get_apiv1_elements()
                        if isinstance(element, Parameter)
                    ],
                    [
                        element
                        for element in mapping.get_apiv2_elements()
                        if isinstance(element, Parameter)
                    ],
                    self.differ.compute_parameter_similarity,
                )
                mappings.extend(new_mapping)
            elif isinstance(mapping.get_apiv1_elements()[0], Result) and isinstance(
                mapping.get_apiv2_elements()[0], Result
            ):
                new_mapping = self._get_mappings_for_api_elements(
                    [
                        element
                        for element in mapping.get_apiv1_elements()
                        if isinstance(element, Result)
                    ],
                    [
                        element
                        for element in mapping.get_apiv2_elements()
                        if isinstance(element, Result)
                    ],
                    self.differ.compute_result_similarity,
                )
                mappings.extend(new_mapping)
            if new_mapping is not None and len(new_mapping) > 0:
                self.differ.notify_new_mapping(new_mapping)
    else:
        mappings.extend(
            self._get_mappings_for_api_elements(
                list(self.apiv1.classes.values()),
                list(self.apiv2.classes.values()),
                self.differ.compute_class_similarity,
            )
        )
        mappings.extend(
            self._get_mappings_for_api_elements(
                list(self.apiv1.functions.values()),
                list(self.apiv2.functions.values()),
                self.differ.compute_function_similarity,
            )
        )
        mappings.extend(
            self._get_mappings_for_api_elements(
                list(self.apiv1.parameters().values()),
                list(self.apiv2.parameters().values()),
                self.differ.compute_parameter_similarity,
            )
        )

        mappings.extend(
            self._get_mappings_for_api_elements(
                [
                    attribute
                    for class_ in self.apiv1.classes.values()
                    for attribute in class_.instance_attributes
                ],
                [
                    attribute
                    for class_ in self.apiv2.classes.values()
                    for attribute in class_.instance_attributes
                ],
                self.differ.compute_attribute_similarity,
            )
        )

        mappings.extend(
            self._get_mappings_for_api_elements(
                [
                    result
                    for function in self.apiv1.functions.values()
                    for result in function.results
                ],
                [
                    result
                    for function in self.apiv2.functions.values()
                    for result in function.results
                ],
                self.differ.compute_result_similarity,
            )
        )
    mappings.extend(self.differ.get_additional_mappings())
    mappings.sort(key=Mapping.get_similarity, reverse=True)
    return mappings