Hierarchical structures (<structList> elements) are used to create
        hierarchically nested annotation graphs (e.g. to express consists-of
        relationships or dominance-edges in syntax trees, RST).
        A <struct> element will be created for each hierarchical node
        (e.g. an NP) with edges (<rel> elements) to each dominated element
        (e.g. tokens, other <struct> elements).

        NOTE: The types/labels of these newly create hierarchical nodes and
        edges aren't stored in this file, but in feat/multiFeat files
        referencing this one! See: __gen_struct_anno_files() and
        __gen_rel_anno_file()).

        There will be one hierarchy file for each top level layer.
        TODO: check, if we can omit hierarchy files for layers that don't
              contain dominance edges

def __gen_hierarchy_file(self, layer):
        """
        Hierarchical structures (<structList> elements) are used to create
        hierarchically nested annotation graphs (e.g. to express consists-of
        relationships or dominance-edges in syntax trees, RST).
        A <struct> element will be created for each hierarchical node
        (e.g. an NP) with edges (<rel> elements) to each dominated element
        (e.g. tokens, other <struct> elements).

        NOTE: The types/labels of these newly create hierarchical nodes and
        edges aren't stored in this file, but in feat/multiFeat files
        referencing this one! See: __gen_struct_anno_files() and
        __gen_rel_anno_file()).

        There will be one hierarchy file for each top level layer.
        TODO: check, if we can omit hierarchy files for layers that don't
              contain dominance edges
        """
        paula_id = '{0}.{1}.{2}_{3}'.format(layer, self.corpus_name, self.name,
                                        layer)
        self.paulamap['hierarchy'][layer] = paula_id
        E, tree = gen_paula_etree(paula_id)

        dominance_edges = select_edges_by(
            self.dg, layer=layer, edge_type=EdgeTypes.dominance_relation,
            data=True)
        span_edges = select_edges_by(
            self.dg, layer=layer, edge_type=EdgeTypes.spanning_relation,
            data=True)
        dominance_dict = defaultdict(lambda: defaultdict(str))
        for source_id, target_id, edge_attrs in dominance_edges:
            if source_id != layer+':root_node':
                dominance_dict[source_id][target_id] = edge_attrs

        # in PAULA XML, token spans are also part of the hierarchy
        for source_id, target_id, edge_attrs in span_edges:
            if istoken(self.dg, target_id):
                dominance_dict[source_id][target_id] = edge_attrs

        # NOTE: we don't add a base file here, because the nodes could be
        # tokens or structural nodes
        slist = E('structList', {'type': layer})
        for source_id in dominance_dict:
            struct = E('struct',
                       {'id': str(source_id)})
            if self.human_readable:
                struct.append(Comment(self.dg.node[source_id].get('label')))

            for target_id in dominance_dict[source_id]:
                if istoken(self.dg, target_id):
                    href = '{0}.xml#{1}'.format(self.paulamap['tokenization'],
                                              target_id)
                else:
                    href = '#{0}'.format(target_id)

                rel = E(
                    'rel',
                    {'id': 'rel_{0}_{1}'.format(source_id, target_id),
                     'type': dominance_dict[source_id][target_id]['edge_type'],
                     XLINKHREF: href})
                struct.append(rel)
                if self.human_readable:
                    struct.append(
                        Comment(self.dg.node[target_id].get('label')))
            slist.append(struct)
        tree.append(slist)
        self.files[paula_id] = tree
        self.file2dtd[paula_id] = PaulaDTDs.struct
        return paula_id