tags:

views:

134

answers:

4

Hey guys,

I'd like to simulate a flag in an xslt script. The idea is for template foo to set a flag (or a counter variable, or anything), so that it can be accessed from template bar. Bar isn't called from foo, but from a common parent template (otherwise I would pass a parameter to it). The structure is like this:

<xsl:template match="bla">
  <xsl:apply-templates select="foo"/> <!-- depending on the contents of foo... -->
  <xsl:apply-templates select="bar"/> <!-- ... different things should happen in bar -->
</xsl:template>

Any tricks are much appreciated.

+2  A: 

Not really... At least not in the sense that you are trying to do it. Variables in XSLT are immutable, once you assign them a value, you can't change them, so trying to call foo multiple times to change the value of the flag wouldn't work. There are a couple of patterns you could try, which might accomplish what you are trying to do, for example:

<xsl:variable name="myFlag"><xsl:apply-templates select="foo" /></xsl:variable>

<xsl:template match="bla">
      <xsl:apply-templates select="bar" /> <!-- Can use the value of $myFlag --.
</xsl:template>

Which will work if template foo is built to return the value of the flag, however, if the value of the flag is meant to change over time, the only real way you can accomplish this is to incorporate the call to foo into the bar template.

<xsl:template match="bla">
    <xsl:apply-templates select="bar"> />
</xsl:template>

<xsl:template match="bar">
   <xsl:variable name="flag"><xsl:apply-templates name="foo" /></xsl:variable>

   <xsl:if test="$flag=1">

   </xsl:if>
</xsl:template>
LorenVS
+1  A: 

There are plenty ways of doing this. For instance:

  • You can use conditional structures like xsl:if/xsl:choose.
  • You can use variables to store whatever is calculated from foo and pass it as parameter to the apply-templates on bar.

The true XSLT way whould be to define different templates for bar - which match different foo cases:

<xsl:template match="bar[../foo[@a='x']]">
  ...
</xsl:template>
<xsl:template match="bar[../foo[@a='y']]">
  ...
</xsl:template>
lexicore
A: 

If template foo shall produce output, any solution using the output as flag won't work. In this case, if you are using a Java based XSLT processor (e.g. Saxon or Xalan), you can use mutable Java objects.

But note that this has its own difficulties. The transform given below uses a global flag, which might not suffice for all use cases. I would like to instantiate the flag in the bla template and pass it as parameter to foo and bar, but I could not get that working in Xalan. Also note that I invoke the Java setter in an xsl:value-of, because otherwise the call might get optimized away (see http://stackoverflow.com/questions/2631301/cannot-access-updated-java-object-from-saxon-xslt-processor/2635901#2635901).

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:myflag="java:mypackage.MyFlag">

<xsl:variable name="foo-flag" select="myflag:new()" />

<xsl:template match="bla">
  <xsl:apply-templates select="foo"/> <!-- depending on the contents of foo... -->
  <xsl:apply-templates select="bar"/> <!-- ... different things should happen in bar -->
</xsl:template>

<xsl:template match="foo">
  <xsl:choose>
     <xsl:when ...>
       <xsl:value-of select="myflag:set($foo-flag, true())" />
       ...
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="myflag:set($foo-flag, false())" />
       ...
     </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="bar">
  <xsl:choose>
     <xsl:when test="myflag:get($foo-flag)">
       ...
     </xsl:when>
     <xsl:otherwise>
       ...
     </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:transform>

The class MyFlag in its most basic version is merely a mutable boolean wrapper.

public class MyFlag {
private boolean flag;
public void set(boolean flag){
  this.flag = flag;
}
public boolean get(){ return flag; }
}
Christian Semrau
A: 

This uses the approach you mentioned in your question: Pass a parameter, from foo to bar. NOTE: This assumes that there is exactly one foo below each bla, or else the bar template will be invoked never or more than once per bar element.

<xsl:template match="bla">
    <xsl:apply-templates select="foo" />
</xsl:template>

<xsl:template match="foo">
   ...    
   <xsl:apply-templates select="../bar">
     <xsl:with-param name="flag" select="..." />
   </xsl:apply-templates />
</xsl:template>
Christian Semrau