This can be solved pretty easily using XPath. Here's the expression you're looking for: count((.|preceding-sibling::ROLE)[not(@name = preceding-sibling::ROLE/@name)])
This can be broken down to make it more readable, as I've done in the following XSLT 1.0 stylesheet:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- don't copy whitespace -->
<xsl:template match="text()"/>
<xsl:template match="ROLE">
<xsl:variable name="roles-so-far" select=". | preceding-sibling::ROLE"/>
<!-- Only select the first instance of each ROLE name -->
<xsl:variable name="roles-so-far-unique"
select="$roles-so-far[not(@name = preceding-sibling::ROLE/@name)]"/>
<xsl:apply-templates select="@name"/>
<xsl:text> </xsl:text>
<xsl:value-of select="count($roles-so-far-unique)"/>
<xsl:text>
</xsl:text> <!-- linefeed -->
</xsl:template>
</xsl:stylesheet>
Here's an alternative implementation, using the Muenchian method. First, declare a key:
<xsl:key name="roles" match="ROLE" use="@name"/>
Then, replace the definition of $roles-so-far-unique with something like this:
<!-- Among all the ROLEs having one of the names so far,
select only the first one for each name -->
<xsl:variable name="roles-so-far-unique"
select="../ROLE[@name = $roles-so-far/@name]
[generate-id(.) = generate-id(key('roles',@name)[1])]"/>
This code, of course, is more complicated. Unless you have a large data set requiring you to speed up processing using the Muenchian method (even then I would test to make sure it buys you anything), you might as well stick with the simpler version above.
Finally, in XSLT 2.0, it's much easier. Simple replace the $roles-so-far-unique definition with the following:
<!-- Return a list of distinct string values, with duplicates removed -->
<xsl:variable name="roles-so-far-unique"
select="distinct-values($roles-so-far/@name)"/>
I hope this has helped you identify where you went wrong in the various attempts that you mentioned.