tags:

views:

844

answers:

4

Is there a way to write an XSL 1.0 template which is matching in all modes?

Or do I have to write a separate template for every existing mode (including additional templates for modes being added in the future)?

Here is what I have:

<xsl:apply-templates mode="mode1" />
    ...
<xsl:apply-templates mode="mode2" />
    ...
<!-- Do not process text content of nodes no matter in what mode -->
<!-- Is there a way to have only one template here? -->
<xsl:template match="text()" mode="mode1" />
<xsl:template match="text()" mode="mode2" />
+2  A: 

The predefined mode: #all (only available in XSLT 2.0 however).

edit: replicating shared mode behaviour with 1.0

<xsl:template match="/">
 <xsl:variable name="choice" select="'a'"/><!-- input seed here -->
 <xsl:choose>
  <xsl:when test="$choice='a'">
   <xsl:apply-templates mode="a"/>
  </xsl:when>
  <xsl:when test="$choice='b'">
   <xsl:apply-templates mode="b"/>
  </xsl:when>
 </xsl:choose>
</xsl:template>

<xsl:template match="*" mode="a">
 [A]
 <xsl:apply-templates />
</xsl:template>

<xsl:template match="*" mode="b">
 [B]
 <xsl:apply-templates />
</xsl:template>

<xsl:template match="text()">
 [ALL]
</xsl:template>
annakata
That would be really nice to have! Thanks for the answer, but it's seems XSLT 2.0 only and I forgot to mention that I'm restricted using XSLT 1.0 :-( Seems I will have to write a separate template anyway...
0xA3
Ah, in that case you should at least be able to wrap the common code in a named or modeless template you can call from within the two mode forks.
annakata
@annakata I don't see how your XSLT 1.0 code would act as if there is a template that is selected in all modes. Could you, please, edit your answer and explain?
Dimitre Novatchev
As I understand your workaround works only nicely unless you have other modeless templates, e.g. <xsl:template match="someNode"> which might get triggered by the additional <xsl:apply-templates />
0xA3
oh yeah, it's *very* situational, but for some specific cases it can work - I'm really just saying that there are ways of getting the commonality into a template and minimising duplication. The worst case scenario though is still that you could use named templates instead of apply-templates
annakata
+2  A: 

If you want to have the template match in all modes then why are you using mode? If you don't use mode then the template will be used all the time. The reason for mode is to conditionally do different things with the same data type. Seems like you want modeless.

Alex
not quite, if you were already within a mode fork (for some useful purpose) default mode templates will not be referenced
annakata
No, I need several modes to process nodes several times doing different things. However, I don't want to process the content of certain nodes in any mode. The only solution seems to write a separate template for each mode.
0xA3
(continued) And a modeless template will only match a modeless apply-templates "call".
0xA3
+1  A: 

Is there a way to write an XSL 1.0 template which is matching in all modes

Yes, in order to do this one should follow these two rules:

  1. Write your template without a mode attribute.

  2. Within the moded templates have an <xsl:apply-templates> instruction without a mode attribute that will result in the template in 1. above being selected for processing

This follows directly from the XSLT 1.0 spec, which says:

If an xsl:apply-templates element has a mode attribute, then it applies only to those template rules from xsl:template elements that have a mode attribute with the same value; if an xsl:apply-templates element does not have a mode attribute, then it applies only to those template rules from xsl:template elements that do not have a mode attribute.

To summarise: A set of templates each in a different mode can still issue <xsl:apply-templates> in such a way (described above), so that the same specific, single template will be selected for processing in each case.

Dimitre Novatchev
This might work in the simple example presented here. However, this might have side-effect when there is more than one modeless template. The exacet behaviour of XSLT 2.0's #all mode is not available in XSLT 1.0 and can hardly be simulated.
0xA3
@divo I will not recommend to anyone to use #all in XSLT 2.0 -- it is too-dangerous. For example, just a little more generic template (matching node()) will interfere with *any other* template. Much better is to design one's templates more carefully, in a more clean way, so that #all is not needed.
Dimitre Novatchev
Yes, it should be used with care, but it depends strongly on the scenario. In my case the content of leave elements should *never* appear in output, hence the empty template(s) matching text(). I don't see the problem with a more generic template though, since "more specific" have higher priority.
0xA3
@divo THis is more complicated, especially if templates are imported.
Dimitre Novatchev
@divo You don't need empty templates matching text() if you use <xsl:apply-templates select="node()[not(self::text())]"/> instead of using <xsl:apply-templates/>
Dimitre Novatchev
A: 

You can simulate your own mode-mechanism by passing a param and checking for that param in every template, just like that:

<xsl:template match="/">
    <xsl:apply-templates>
       <xsl:with-param name="mode">mode1</xsl:with-param>
    </xsl:apply-templates>

    <xsl:apply-templates>
        <xsl:with-param name="mode">mode2</xsl:with-param>
    </xsl:apply-templates>
</xsl:template>

<xsl:template match="text()" />

<xsl:template match="condition1">
    Matches in every mode.
</xsl:template>

<xsl:template match="condition2">
    <xsl:param name="mode" />
    <xsl:choose>
        <xsl:when test="$mode = 'mode1' or $mode = 'mode2'">
             Matches in Mode 1 and Mode 2
        </xsl:when>
        <xsl:when test="$mode = 'mode3'">
             Matches in Mode 3
        </xsl:when>
        <xsl:otherwise>Matches all other Modes</xsl:otherwise>
    </xsl:choose>
</xsl:template>

You could advance things even further by automatically generating XSL-T. Says: If you want some syntactic sugar or feature XSL 1.0 lacks you can extend your Stylesheet and write an XSL-T which translates the functionality into XSL 1.0.

For example, use your existing templates but instead of using mode as an XSL-T attribute introduce your own element, say <your:mode> and annotate existing templates. Another XSL-T might then transform that extended version into XSL 1.0 by simply copying contents and reproducing templates as needed, or into a format like shown in the example above.

It's quite tricky but works pretty well.

Julian Fleischer