This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 9722 - [XSLT 2.1] Enhancement to conditional modes
Summary: [XSLT 2.1] Enhancement to conditional modes
Status: CLOSED WONTFIX
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 3.0 (show other bugs)
Version: Working drafts
Hardware: PC All
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-05-12 20:08 UTC by Evan Lenz
Modified: 2014-05-15 14:00 UTC (History)
0 users

See Also:


Attachments

Description Evan Lenz 2010-05-12 20:08:37 UTC
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.
Comment 1 Michael Kay 2010-05-12 20:35:19 UTC
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.
Comment 2 Michael Kay 2010-05-12 20:35:19 UTC
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.
Comment 3 Evan Lenz 2010-05-12 21:44:28 UTC
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".
Comment 4 Evan Lenz 2010-05-12 22:05:20 UTC
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.)
Comment 5 Michael Kay 2010-07-16 15:35:11 UTC
Thanks for the input, Evan. We looked at it carefully and felt that given the complexity involved, the requirements this meets are rather specialized.