Reputation: 451
This is a "I want my cake and eat it" question, and there are lots of variables at play but I will do my best to keep it succinct.
Our software product has the notion of three tiers of zones [A, B and C]. Our customer's product only has one tier of zone [X]. The customer's XML file always contains 2000 zones, even if only a handful are actually assigned to devices. For reasons not pertinent to this question, I must strip out unused zones from our XSLT output. This output is used as the configuration of one of our software modules.
I have this (excerpt) in my xsl file:
<xsl:for-each select="zoneX">
<xsl:if test="/{path}/device[zone=current()/@id]">
<xsl:call-template name="ZoneTypeA"/>
<xsl:call-template name="ZoneTypeB"/>
<xsl:call-template name="ZoneTypeC"/>
</xsl:if>
</xsl:for-each>
This will generate an output like this (example):
<zoneA id="0" .../>
<zoneB id="0" .../>
<zoneC id="0" .../>
<zoneA id="1" .../>
<zoneB id="1" .../>
<zoneC id="1" .../>
Because in this example only zoneX[id=0] and zoneX[id=1] are assigned to a device.
I must stress at this point that our software module has absolutely no problems with this particular sequencing of these elements.
Where my 'OCD' kicks in (and that of peers) is that our example configs and our documentation on our configs always group the zone types together.
As such the desired output would read:
<zoneA id="0" .../>
<zoneA id="1" .../>
<zoneB id="0" .../>
<zoneB id="1" .../>
<zoneC id="0" .../>
<zoneC id="1" .../>
(empty lines added for clarity)
I have tried repeating the <for-each>
for each zone type:
<xsl:for-each select="zones/zone">
<xsl:if test="/{long-path}/devices[zone=current()/@id]">
<xsl:call-template name="ZoneTypeA"/>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="zones/zone">
<xsl:if test="/{long-path}/devices[zone=current()/@id]">
<xsl:call-template name="ZoneTypeB"/>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="zones/zone">
<xsl:if test="/{long-path}/devices[zone=current()/@id]">
<xsl:call-template name="ZoneTypeC"/>
</xsl:if>
</xsl:for-each>
This gives me the nice looking output, but triples the time it takes to generate the output.
Is there another way where I only have to iterate the devices once, yet generate output where the zone types are grouped together?
EDIT
Kept it too succinct and missed off an important detail. Apos.
A lot of time is spent finding a device for each zone. See the <xsl:if>
in my examples above.
Real scenario example: The customer config has 5000+ devices in it. Most zones comprise multiple devices. Only 200 (of the 2000) zoneX are used.
In my first solution, I only iterate the devices once per zoneX.
Upvotes: 0
Views: 42
Reputation: 70598
One way, would be to have another named template that contains the xsl:for-each
that can be a passed a parameter with the zone type...
<xsl:template name="AllZones">
<xsl:param name="zoneType" />
<xsl:for-each select="zones/zone">
<xsl:if test="/{long-path}/devices[zone=current()/@id]">
<xsl:choose>
<xsl:when test="$zoneType='A'"><xsl:call-template name="ZoneTypeA"/></xsl:when>
<xsl:when test="$zoneType='B'"><xsl:call-template name="ZoneTypeB"/></xsl:when>
<xsl:when test="$zoneType='C'"><xsl:call-template name="ZoneTypeC"/></xsl:when>
</xsl:choose>
</xsl:if>
</xsl:for-each>
</xsl:template>
And you would call it three times, like so....
<xsl:call-template name="AllZones">
<xsl:with-param name="zoneType" select="'A'" />
</xsl:call-template>
<xsl:call-template name="AllZones">
<xsl:with-param name="zoneType" select="'B'" />
</xsl:call-template>
<xsl:call-template name="AllZones">
<xsl:with-param name="zoneType" select="'C'" />
</xsl:call-template>
How different is the code in the three named templates ZoneTypeA, ZoneTypeB and ZoneTypeC? If the code is similar, you could perhaps combine them into a single template, and pass in the zone type as a parameter, avoiding the need for all the separate xsl:call-templates
too.
Upvotes: 1