XSLT 1.0 doesn’t have user definable functions, the only way to return values is as output which can be captured in an
<xsl:variable name="captured"><xsl:call-template name="generate-output"/></xsl:variable>
And, naturally, the tree recursion was already generating output and could not easily be used to return a count of nodes processed so far as well.
After a couple of hours of struggling and some syntax checking with my brother Ben (for things like $node/ancestors::* - thanks bro) we have a fragment that does the trick in one line!
count($node/preceding::rule[ancestor::*[generate-id()=$ancestorid]])
This is more clearly excodessed as:
count($node/preceding::rule[count(ancestor::*[generate-id()=$ancestorid])])
and in english reads as: to find the sequence number for $node, count all preceding nodes who are contained by the common ancestor identified by $ancestorid. Without the codedicate, it would count all preceding nodes in the entire document, instead of those with a certain ancestor.
However this is not complete. preceding does not take into account open nodes, i.e. the node to which it is being applied, nor any of it’s ancestors; so this correction is added:
+ count($node/ancestor::rule) - count($ancestor/ancestor::rule)
as the number of non-counted nodes is clearly the difference between the number of ancestors of $node, and the number of ancestors of $ancestor.
The full statement is:
count($node/preceding::rule[ancestor::*[generate-id()=$ancestorid]])
+ count($node/ancestor::rule) - count($ancestor/ancestor::rule)
I leave it to the reader to produce a version that reports the total number "rule" nodes under the current ancestor.
Finally I used this recipe:
<xsl:template name="rule-number">
<xsl:param name="name"><xsl:message terminate="yes">rule-number called without name</xsl:message></xsl:param>
<xsl:param name="rules-node" select="//dbam-bandwidth-rules"/>
<!– take the first occurance of each rule (no preceding of same ancestor) –>
<xsl:variable name="all_rules" select="$rules-node//rule[not(@name = preceding::rule[count(ancestor::*[generate-id()=generate-id($rules-node)]) > 0]/@name)]"/>
<xsl:for-each select="$all_rules">
<xsl:if test="@name=$name"><xsl:value-of select="position()"/></xsl:if>
</xsl:for-each>
</xsl:template>