generate-id() in xsl:attribute-set

Inasmuch as xsl:attribute-set is most often thought of for adding constant sets of XSL-FO properties, it’s easy to forget that, as it says in the XSLT 2.0 spec:

Evaluating the same attribute set more than once can produce different results, because although an attribute set does not have parameters, it may contain expressions or instructions whose value depends on the evaluation context.

This changing context was useful recently when customising an existing XSL-FO stylesheet included adding id properties for use as the targets of fo:basic-link cross-references. Since I couldn’t modify the base stylesheet, I was looking at having to replicate multiple templates just to add the id properties where needed. However, to make it easy to customise the base stylesheet, its templates for the elements of interest each referred to named attribute sets and, more often than not, I was already adding to the same attribute sets in my stylesheet that imported the base stylesheet. It was simple, therefore, to add the xsl:attribute for the id property to the customisations of the attribute sets and be able to leave the base stylesheet’s templates unaltered.

The short form of what I was doing may be seen as:

<xsl:attribute-set name="fig">
  <xsl:attribute name="id" select="generate-id()" />
</xsl:attribute-set>

where the changing context for each time the attribute set is used produces a different id attribute each time, each containing the unique ID for the then-current context node.

However, the source XML had id attributes on some elements already, and I wanted to hook into using the existing machinery in the base stylesheet for getting either the assigned ID or a generated ID, so the code in the customising stylesheet was more like:

<xsl:attribute-set name="fig">
  <xsl:attribute name="id">
    <xsl:call-template name="get-id" />
  </xsl:attribute>
</xsl:attribute-set>

<xsl:template name="assign-id">
  <xsl:param name="node" select="."/>
  <xsl:attribute name="id">
    <xsl:call-template name="get-id">
      <xsl:with-param name="node" select="$node"/>
    </xsl:call-template>
  </xsl:attribute>
</xsl:template>
  
<xsl:template name="get-id">
  <xsl:param name="node" select="."/>
  <xsl:apply-templates select="$node" mode="id"/>
</xsl:template>

where the assign-id template overrides the corresponding template in the base stylesheet and part of the function of the original assign-id is broken out into get-id for use in the attribute sets. And did I mention that the base stylesheet, and therefore also its customisation, was written in XSLT 1.0 for maximum portability?