В XSLT 2.0 было бы довольно легко с новыми функциями группировки.
В XSLT 1.0 это немного сложнее, но это работает:
<xsl:template match="/tree">
<xhtml>
<head/>
<body>
<ul>
<xsl:apply-templates select="node[depth='0']"/>
</ul>
</body>
</xhtml>
</xsl:template>
<xsl:template match="node">
<xsl:variable name="thisNodeId" select="generate-id(.)"/>
<xsl:variable name="depth" select="depth"/>
<xsl:variable name="descendants">
<xsl:apply-templates select="following-sibling::node[depth = $depth + 1][preceding-sibling::node[depth = $depth][1]/generate-id() = $thisNodeId]"/>
</xsl:variable>
<li>
<xsl:value-of select="name"/>
</li>
<xsl:if test="$descendants/*">
<ul>
<xsl:copy-of select="$descendants"/>
</ul>
</xsl:if>
</xsl:template>
Суть дела - длинная и уродливая переменная «потомков», которая ищет узлы после текущего узла, у которых «глубина» дочернего элемента больше текущей глубины, но не после другого узла, который будет иметь такой же глубина как текущая глубина (потому что если бы они были, они были бы дочерними элементами этого узла вместо текущего).
Кстати, в вашем примере с ошибкой: «FLASH» должен быть потомком «MP3 PLAYERS», а не «родным братом».
EDIT
Фактически (как упомянуто в комментариях), в «чистом» XSLT 1.0 это не работает по двум причинам: выражение пути неправильно использует generate-id (), и нельзя использовать «фрагмент дерева результатов» в выражение пути.
Вот правильная версия шаблона узла "XSLT 1.0" (успешно протестированная с Saxon 6.5), которая не использует ни EXSLT, ни XSLT 1.1:
<xsl:template match="node">
<xsl:variable name="thisNodeId" select="generate-id(.)"/>
<xsl:variable name="depth" select="depth"/>
<xsl:variable name="descendants">
<xsl:apply-templates select="following-sibling::node[depth = $depth + 1][generate-id(preceding-sibling::node[depth = $depth][1]) = $thisNodeId]"/>
</xsl:variable>
<xsl:variable name="descendantsNb">
<xsl:value-of select="count(following-sibling::node[depth = $depth + 1][generate-id(preceding-sibling::node[depth = $depth][1]) = $thisNodeId])"/>
</xsl:variable>
<li>
<xsl:value-of select="name"/>
</li>
<xsl:if test="$descendantsNb > 0">
<ul>
<xsl:copy-of select="$descendants"/>
</ul>
</xsl:if>
</xsl:template>
Конечно, нужно учитывать повторяющееся выражение пути, но без возможности превращать «фрагменты дерева результатов» в XML, который действительно может быть обработан, я не знаю, возможно ли это? (написание пользовательской функции, конечно, поможет, но гораздо проще использовать EXSLT)
Итог: используйте XSLT 1.1 или EXSLT, если можете!
2-е редактирование
Чтобы не повторять выражение пути, вы также можете полностью забыть тест, который просто приведет к некоторому пустому значению, которое вы можете либо оставить в результате, либо в постобработке для устранения.