Return a BubbleTree instance.

        bbldata -- lines in bubble bbltree
        oriented -- True: returned BubbleTree is oriented

def from_bubble_data(bbldata:iter, oriented:bool=False,
                         symmetric_edges:bool=True) -> 'BubbleTree':
        """Return a BubbleTree instance.

        bbldata -- lines in bubble bbltree
        oriented -- True: returned BubbleTree is oriented

        """
        # get structure as two dicts
        edges, inclusions = defaultdict(set), defaultdict(set)
        used_in_edges = set()
        for line in bbldata:
            if not line: continue
            ltype, *payload = line
            if ltype == 'EDGE':
                source, target = payload
                edges[source].add(target)
                used_in_edges.add(source)
                used_in_edges.add(target)
            elif ltype == 'SET':
                setname = payload[0]
                inclusions[setname]  # create it if not already populated
            elif ltype == 'NODE':
                nodename = payload[0]
                inclusions[nodename] = ()  # a node can't contain anything
            elif ltype == 'IN':
                contained, container = payload
                inclusions[container].add(contained)
            else:  # comment, empty or error
                if ltype not in {'COMMENT', 'EMPTY', 'ERROR'}:
                    raise ValueError("The following line is not a valid "
                                     "type ({}): '{}'".format(ltype, payload))
                else:  # it's a comment, an empty line or an error
                    pass


        # all (power)nodes used in edges should be present in inclusions tree
        for node in used_in_edges:
            if node not in inclusions:  # contains nothing, so its a node
                inclusions[node] = ()

        # all pure nodes needs to be a key in inclusions
        for node in set(it.chain.from_iterable(inclusions.values())):
            # an element that is not in inclusion is either:
            #  - a node not explicitely defined in a NODE line
            #  - a powernode that contains nothing and not explicitely defined in a SET line
            # the second case is meaningless : this is the case for any unused powernode name.
            # Consequently, elements not in inclusions are nodes.
            if node not in inclusions:
                inclusions[node] = ()

        # find the roots
        not_root = set(it.chain.from_iterable(inclusions.values()))
        roots = frozenset(frozenset(inclusions.keys()) - not_root)

        # build the (oriented) bubble tree
        symmetric_edges = symmetric_edges and not oriented
        if symmetric_edges:
            edges = utils.completed_graph(edges)
        return BubbleTree(edges=edges, inclusions=dict(inclusions),
                          roots=roots, oriented=oriented, symmetric_edges=symmetric_edges)