Hi Sam,
Here's another approach you might want to consider. Sometimes when
considering how to implement something, I ask myself the question, "What
is the most direct/simplest way of expressing my intention?" In this
case, my intention is to add the default attribute revision="0" to the
elements <Method>, <Cal>, and <Blank>.
So I would like to write something as concise and direct as this:
<xsl:template mode="default-attributes" match="Method | Cal | Blank">
<xsl:attribute name="revision">0</xsl:attribute>
</xsl:template>
And it turns out I can do exactly that, provided that I implement the
generic identity rule with a hook for adding default attributes:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates mode="default-attributes" select="."/>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- By default, don't add any default attributes -->
<xsl:template mode="default-attributes" match="*"/>
That way, it's a very simple process to add more default attributes to
other elements. Just provide another rule in the "default-attributes" mode.
This pattern can be extended to other aspects of the identity rule that
you want to override. For example, the following implementation of the
identity rule lets you pinpoint exactly what part you want to override,
whether it be an element's contents, its attributes, any default
attributes, or an attribute's value. To override something, just supply
a rule in the applicable mode ("content", "attributes",
"default-attributes", or "attribute-value", respectively). Such hooks
allow me to avoid repeating myself later.
<!-- Identity rule for child nodes -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates mode="default-attributes" select="."/>
<xsl:apply-templates mode="attributes" select="."/>
<xsl:apply-templates mode="content" select="."/>
</xsl:copy>
</xsl:template>
<!-- By default, don't add default attributes -->
<xsl:template mode="default-attributes match="*"/>
<!-- By default, process all of the element's attributes -->
<xsl:template mode="attributes" match="*">
<xsl:apply-templates select="@*"/>
</xsl:template>
<!-- By default, process the element's children -->
<xsl:template mode="content" match="*">
<xsl:apply-templates/>
</xsl:template>
<!-- Identity rule for attributes -->
<xsl:template match="@*">
<xsl:attribute name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates mode="attribute-value" select="."/>
</xsl:attribute>
</xsl:template>
<!-- By default, use the given value of the attribute -->
<xsl:template mode="attribute-value" match="@*">
<xsl:value-of select="."/>
</xsl:template>
I haven't needed to make it more extensible than that, but depending on
the task at hand, I could also imagine providing hooks to override the
name and namespace URI of each node.
I have a feeling you are going to stick with the implementation that
you've already arrived at, because it works, and because (at least with
the XSLT processor that you're using), it outputs the attributes in the
order that you wanted. But I would underscore Michael and David's point
that you should not rely on attributes to appear in any particular order.
Evan Lenz
"How XSLT Works"
http://www.oreilly.com/catalog/xsltpr/chapter/index.html
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--