This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.
I like being able to set mode="#current". It allows me to set the mode conditionally, based on the current mode. But I have repeatedly come across the situation where I want to make the mode conditional (dependent on current) but not be the *same as* current. In other words, I want to dispatch to a different mode depending on what the current mode is. I can think of several ways of designing this into the language (without allowing mode names to be dynamic or directly accessible as values). Let me know your thoughts on this. I have both specific use cases and design ideas, but this is the essence of what I want.
We provide accessor functions for most components of the dynamic context, and I can't immediately see a good reason why we shouldn't provide current-mode(). Allowing the mode attribute on apply-templates to be set dynamically is something that requires more careful thought. A particular difficulty is that we want to know statically whether the apply-templates uses a streaming or non-streaming mode.
Dynamic access to mode names would give me a really ugly way to do what I want... (see Example #2 below). But I think it would be so painful as to defeat the purpose for me (especially since I can already pass a "mode name" via a tunnel parameter at the original point of invocation). What I'd really like to do is refer to the *previous* mode, both in apply-templates and in match patterns. So <xsl:apply-templates/> would now allow mode="#previous" And <xsl:template> would now allow mode="myPreviousMode/myCurrentMode" - limited to one separator ("/" as here or some other non-space, non-QName character). Thus every invocation of apply-templates is conceptually a conditional mode invocation, parameterized by the current mode at the point of invocation. I can think of several ways of modeling this (e.g., a mode can actually be a *pair* of QNames, depending on whether its previous mode is ever referenced in the stylesheet). EXAMPLE #1 (with access to previous mode): <!-- In stage 1, delete <foo>'s content --> <xsl:template mode="stage1/content" match="foo"/> <!-- In stage 3, add <bar/> after <bat/> --> <xsl:template mode="stage3/after" match="bat"> <bar/> </xsl:template> <!-- The boilerplate code that enables me to write the above surgically precise rules --> <!-- By default, copy everything unchanged --> <xsl:template mode="stage1 stage2 stage3" match="*"> <xsl:apply-templates mode="before" select="."/> <xsl:copy> <xsl:apply-templates mode="#current" select="@*"/> <xsl:apply-templates mode="content" select="."/> </xsl:copy> <xsl:apply-templates mode="after" select="."/> </xsl:template> <!-- By default, process children --> <xsl:template mode="content" match="*"> <xsl:apply-templates mode="#previous"/> <!-- Processor knows statically that this will be either "stage1", "stage2", or "stage3" --> </xsl:template> <!-- By default, don't add anything before or after --> <xsl:template mode="before after" match="*"/> EXAMPLE #2 (achieving the same, using only current-mode()): <!-- In stage 1, delete <foo>'s content --> <xsl:template mode="stage1-content" match="foo"/> <!-- In stage 3, add <bar/> after <bat/> --> <xsl:template mode="stage3-after" match="bat"> <bar/> </xsl:template> <!-- The boilerplate code that enables me to write the above surgically precise rules --> <!-- By default, copy everything unchanged --> <xsl:template mode="stage1 stage2 stage3" match="*"> <xsl:variable name="stage1" select="current-mode() eq 'stage1'"/> <xsl:variable name="stage2" select="current-mode() eq 'stage2'"/> <xsl:choose> <xsl:when test="$stage1"> <xsl:apply-templates mode="stage1-before" select="."/> </xsl:when> <xsl:when test="$stage2"> <xsl:apply-templates mode="stage2-before" select="."/> </xsl:when> <xsl:otherwise> <xsl:apply-templates mode="stage3-before" select="."/> </xsl:otherwise> </xsl:choose> <xsl:copy> <xsl:apply-templates mode="#current" select="@*"/> <xsl:choose> <xsl:when test="$stage1"> <xsl:apply-templates mode="stage1-content" select="."/> </xsl:when> <xsl:when test="$stage2"> <xsl:apply-templates mode="stage2-content" select="."/> </xsl:when> <xsl:otherwise> <xsl:apply-templates mode="stage3-content" select="."/> </xsl:otherwise> </xsl:choose> </xsl:copy> <xsl:choose> <xsl:when test="$stage1"> <xsl:apply-templates mode="stage1-after" select="."/> </xsl:when> <xsl:when test="$stage2"> <xsl:apply-templates mode="stage2-after" select="."/> </xsl:when> <xsl:otherwise> <xsl:apply-templates mode="stage3-after" select="."/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- By default, process children --> <xsl:template mode="stage1-content stage2-content stage3-content" match="*"> <xsl:choose> <xsl:when test="current-mode() eq 'stage1-content'"> <xsl:apply-templates mode="stage1"/> </xsl:when> <xsl:when test="current-mode() eq 'stage2-content'"> <xsl:apply-templates mode="stage2"/> </xsl:when> <xsl:otherwise> <xsl:apply-templates mode="stage3"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- By default, don't add anything before or after --> <xsl:template mode="stage1-before stage2-before stage3-before stage1-after stage2-after stage3-after" match="*"/> Another way to look at this is that I want my XSLT processor to do the work of automatically translating Example #1 into Example #2 for me, just as it already does with mode="#current".
A couple of additional thoughts: "/" as the separator would probably be confusing. ">" might be nicer. For streaming vs. non-streaming, I think you already need to solve this problem with mode="#current", right? mode="#previous" is technically no more dynamic than mode="#current". (It resolves to a statically determined, finite list of possibilities.)
Thanks for the input, Evan. We looked at it carefully and felt that given the complexity involved, the requirements this meets are rather specialized.