Я не думаю, что это групповая задача или для нее требуется XSLT 2.0.
Это преобразование:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:apply-templates select="term[not(broaderterm)]">
<xsl:with-param name="pIndent" select="''"/>
<xsl:sort select="title"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="term">
<xsl:param name="pIndent"/>
<xsl:value-of select="concat($pIndent, title, '
')"/>
<xsl:variable name="vSubterms" select=
"../term[broaderterm = current()/title]"/>
<xsl:variable name="vLeafSubterms" select=
"narrowerterm[not(. = /*/term[broaderterm = current()/title]/title)]"/>
<xsl:apply-templates select="$vSubterms | $vLeafSubterms">
<xsl:with-param name="pIndent" select="concat($pIndent, '.')"/>
<xsl:sort select="concat(self::term/title, self::narrowerterm/text())"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="narrowerterm">
<xsl:param name="pIndent"/>
<xsl:value-of select="concat($pIndent, ., '
')"/>
</xsl:template>
</xsl:stylesheet>
при применении кпредоставленный XML-документ :
<thesaurus>
<term>
<title>defense</title>
<narrowerterm>defense skills</narrowerterm>
<narrowerterm>defense actions</narrowerterm>
</term>
<term>
<title>defense skills</title>
<broaderterm>defense</broaderterm>
<narrowerterm>skill cd</narrowerterm>
<narrowerterm>skill xy</narrowerterm>
<narrowerterm>skill ab</narrowerterm>
</term>
<term>
<title>defense actions</title>
<broaderterm>defense</broaderterm>
<narrowerterm>actions against xy</narrowerterm>
<narrowerterm>actions against ab</narrowerterm>
</term>
</thesaurus>
дает требуемый, правильный результат :
defense
.defense actions
..actions against ab
..actions against xy
.defense skills
..skill ab
..skill cd
..skill xy