For interest, here is a draft effort at a solution. It is close, but not quiet right, as you can see from the output, as it uses different grouping rules. I am still trying to understand the required grouping rules. I will update if I get a better understanding.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="xsl exsl">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="phase-1-output">
<xsl:apply-templates select="/" mode="phase-1" />
</xsl:variable>
<xsl:variable name="phase-2-output">
<xsl:apply-templates select="exsl:node-set($phase-1-output)" mode="phase-2" />
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select="$phase-2-output" />
</xsl:template>
<!--================ Phase 1 ===============================-->
<xsl:template match="/" mode="phase-1">
<t>
<xsl:apply-templates select="*/*/*/ITEM/Hierarchy" mode="phase-1" />
</t>
</xsl:template>
<xsl:template match="Hierarchy" mode="phase-1">
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="levels" select="." />
<xsl:with-param name="item" select="../TITLE" />
</xsl:call-template>
</xsl:template>
<xsl:template name="analyze-hierarchy"><!-- phase-1 -->
<xsl:param name="levels" />
<xsl:param name="item" />
<xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
<xsl:variable name="e-level" select="
translate(
substring($level,1,string-length($level) - 1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:choose>
<xsl:when test="$level">
<xsl:element name="{$e-level}">
<xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="levels" select="substring-after($levels,':')" />
<xsl:with-param name="item" select="$item" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<ITEM Name="{$item}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--================ Phase 2 ===============================-->
<xsl:key name="kLevel"
match="*[starts-with(name(),'LEVEL')]"
use="concat(generate-id(..),'|',@name)" />
<xsl:template match="/" mode="phase-2">
<TREE>
<LEVELS>
<xsl:variable name="t" select="concat(generate-id(t),'|')" />
<xsl:apply-templates select="t/LEVEL1[
generate-id() = generate-id( key('kLevel',concat($t,@name))[1])
]" mode="phase-2-head" />
</LEVELS>
</TREE>
</xsl:template>
<xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2-head">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates select="key('kLevel',concat(generate-id(..),'|',@name))" mode="phase-2" />
<xsl:copy-of select="ITEM" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(name(),'LEVEL')]" mode="phase-2">
<xsl:variable name="p" select="concat(generate-id(.),'|')" />
<xsl:apply-templates select="*[starts-with(name(),'LEVEL')][
generate-id() = generate-id( key('kLevel',concat($p,@name))[1])
]" mode="phase-2-head" />
</xsl:template>
</xsl:stylesheet>
...with sample input produces this (not quiet correct output)...
<TREE>
<LEVELS>
<LEVEL1 name="level1A">
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM Name="Office Cleaning" />
</LEVEL3>
</LEVEL2>
<LEVEL2 name="level2A">
<LEVEL3 name="level3A">
<ITEM Name="Office Cleaning1" />
</LEVEL3>
</LEVEL2>
<LEVEL2 name="level2B">
<LEVEL3 name="level3C">
<ITEM Name="Office Cleaning2" />
</LEVEL3>
</LEVEL2>
</LEVEL1>
<LEVEL1 name="level1B">
<LEVEL2 name="level2B">
<LEVEL3 name="level3B">
<ITEM Name="Office Cleaning" />
</LEVEL3>
</LEVEL2>
</LEVEL1>
</LEVELS>
</TREE>
UPDATE
Ok, round 2. I copied Dimitre's grouping rule, which is all or nothing on the content of the Hierarchy element. This solution produces the expected output for the sample input. Note that in contrast to Dimitre's <xsl:element name="LEVEL{$pDepth}">
method, I have derived the LEVEL1 style element names from the Hierarchy steps. I am not sure if this is correct.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="kLevel" match="Hierarchy" use="." />
<xsl:template match="/">
<TREE>
<LEVELS>
<xsl:apply-templates select="*/*/*/ITEM/Hierarchy[
generate-id() = generate-id( key('kLevel',.)[1])
]" mode="group" />
</LEVELS>
</TREE>
</xsl:template>
<xsl:template match="Hierarchy" mode="group">
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="key" select="." />
<xsl:with-param name="levels" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="analyze-hierarchy">
<xsl:param name="key" />
<xsl:param name="levels" />
<xsl:variable name="level" select="substring-before(concat($levels,':'),':')" />
<xsl:variable name="e-level" select="
translate(
substring($level,1,string-length($level) - 1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:choose>
<xsl:when test="$level">
<xsl:element name="{$e-level}">
<xsl:attribute name="name"><xsl:value-of select="$level" /></xsl:attribute>
<xsl:call-template name="analyze-hierarchy">
<xsl:with-param name="key" select="$key" />
<xsl:with-param name="levels" select="substring-after($levels,':')" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('kLevel',$key)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Hierarchy">
<ITEM Name="{../TITLE}" />
</xsl:template>
</xsl:stylesheet>