Skip to content

APIMapping

Source code in src/library_analyzer/processing/migration/model/_api_mapping.py
class APIMapping:
    threshold_of_similarity_between_mappings: float
    threshold_of_similarity_for_creation_of_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,
    ) -> 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

    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], 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)
                # Attribute und Result could be added here
                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,
                ),
            )
            # Attribute und Result could be added here
        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]) -> Mapping | None:
        """
        Return the best mapping from this apiv1 element to apiv2 elements.

        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:
        """
        Prevent that an element in a mapping appears multiple times in a list of mappings.

        Affected mappings are merged and the results included in the list. If there is no such element, the mapping will
        be included without any merge.

        Parameters
        ----------
        mapping_to_be_appended : Mapping
            the mapping that should be included in mappings
        mappings : list[Mapping]
            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: API = apiv1 instance-attribute

apiv2: API = apiv2 instance-attribute

differ: AbstractDiffer = differ instance-attribute

threshold_of_similarity_between_mappings: float = threshold_of_similarity_between_mappings instance-attribute

threshold_of_similarity_for_creation_of_mappings: float = 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)

Source code in src/library_analyzer/processing/migration/model/_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,
) -> 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

map_api()

Source code in src/library_analyzer/processing/migration/model/_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], 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)
            # Attribute und Result could be added here
            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,
            ),
        )
        # Attribute und Result could be added here
    mappings.extend(self.differ.get_additional_mappings())
    mappings.sort(key=Mapping.get_similarity, reverse=True)
    return mappings