def get_leaves(self, item_ids=None, language=None, forbidden_item_ids=None):
        """
        Get mapping of items to their reachable leaves. Leaves having
        inactive relations to other items are omitted.

        Args:
            item_ids (list): items which are taken as roots for the reachability
            language (str): if specified, filter out items which are not
                available in the given language

        Returns:
            dict: item id -> list of items (reachable leaves)
        """
        forbidden_item_ids = set() if forbidden_item_ids is None else set(forbidden_item_ids)
        children = self.get_children_graph(item_ids, language=language, forbidden_item_ids=forbidden_item_ids)
        counts = self.get_children_counts(active=None)
        if item_ids is None:
            # not leaves
            item_ids = set(children.keys())

        def _get_leaves(item_id):
            leaves = set()

            def __search(item_ids):
                result = set(flatten([children.get(item_id, []) for item_id in item_ids]))
                new_leaves = {item_id for item_id in result if item_id not in children.keys()}
                leaves.update(new_leaves)
                return result - new_leaves

            fixed_point(
                is_zero=lambda to_visit: len(to_visit) == 0,
                minus=lambda to_visit, visited: to_visit - visited,
                plus=lambda visited_x, visited_y: visited_x | visited_y,
                f=__search,
                x={item_id}
            )
            leaves = {leaf for leaf in leaves if counts[leaf] == 0}
            if len(leaves) > 0:
                return leaves
            if counts[item_id] == 0 and item_id not in forbidden_item_ids:
                return {item_id}
            return set()

        return {item_id: _get_leaves(item_id) for item_id in item_ids}