1

I'm using XSLT to convert 2 XML files into C code (XSLT 2.0).

The goal is to generate the #include required for the .c file (could be many).

There are 2 different places I need to look for the variable portion of the #include name: one can be read straight from one XML file, but the second needs to search the other file once we have found the first (both are generated XML files, yay for inherited code!).

    <xsl:for-each select="/root/eventDoc/event/parameterList/parameter">
        <xsl:variable name="name" select="@name"/>
        <xsl:variable name="module" select="@module"/>
        <xsl:variable name="refModule" select="eventgen:ref-module-lookup($name, $module)"/>

Currently I simply print $module and $refModule in an include like so (simplified by removing my current work to avoid duplicates):

    <xsl:text>
    #include "agent/gen/</xsl:text><xsl:value-of select="concat(fn:lower-case(translate($module, '-', '_')),'.h')"/><xsl:text>"</xsl:text>

This works okay, but I want to get rid of the inevitable duplicates. If I was simply writing C code, I would create an array and add each unique $module and $refModule and then walk through it and print each out.

My first approach had me using preceding::parameter/@module to avoid duplicates, the problem is that preceding::parameter/@refModule doesn't exist because it doesn't belong to this XML file! preceding::parameter/$refModule didn't work either (not 100% clear on variable scope in XSLT).

I think I can deal with the unique part, but my question is how can I store these non-XML values so that I can go through them after my for-each and print them out? Templates? Perhaps I don't need to store after all?

Note: I can provide XML input, but I don't think it would be helpful for my question (let me know)

Thanks!

Edit: While already solved, adding some additional information for any unlucky person who has a similar problem.

Input XML one:

<event name="PolicyError">
<!-- list of parameters -->
  <parameterList>
      <parameter
        name="Index" module="COMPANY-TEAM-NG-MIB"
      />        
      <parameter
        name="PolicyId" module="COMPANY-TEAM-NG-MIB"
      />
  </parameterList>
</event>

desired output:

#include "agent/gen/company_team_ng_mib.h"

In the interest of simplicity, I've left out the eventgen:ref-module-lookup function and the second XML file, but it basically uses the given name and module to lookup the root module (refModule) in which name is defined

whoombat
  • 43
  • 4
  • I think you would get better answers if you presented a [minimal, complete and verifiable sample of your code](http://stackoverflow.com/help/mcve); complete minimal input files, a complete minimal stylesheet and the complete output you expect for those specific inputs. – Mathias Müller Mar 24 '15 at 22:13

2 Answers2

2

You've not given an example I can test this with, but if you can get all the possible values into a single sequence then you can remove duplicates using distinct-values, for example:

<xsl:variable name="moduleNames" select="distinct-values(/root/eventDoc/event
  /parameterList/parameter/(@module, eventgen:ref-module-lookup(@name, @module)))"/>

<xsl:for-each select="$moduleNames">
  <xsl:value-of select="concat('&#10;#include &quot;agent/gen/',
                               lower-case(translate(., '-', '_')),
                               '.h&quot;')"/>
</xsl:for-each>

Note that distinct-values does not provide any ordering guarantees. If this is a problem (i.e. you need the #include directives to appear in the same order as the parameters in the XML) then you could use for-each-group instead:

<xsl:variable name="moduleNames" select="/root/eventDoc/event
  /parameterList/parameter/(@module, eventgen:ref-module-lookup(@name, @module))"/>

<xsl:for-each-group select="$moduleNames" group-by=".">
  <xsl:value-of select="concat('&#10;#include &quot;agent/gen/',
                               lower-case(translate(., '-', '_')),
                               '.h&quot;')"/>
</xsl:for-each-group>

The result of processing the various groups is guaranteed to be output in the order of the group's first mention in the input sequence.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
0

For the sake of completeness, here is the solution that I ended up going with. Ian got the solution 99% of the way there, I just needed to sort my #includes alphabetically.

  <xsl:variable name="moduleNames" select="distinct-values(/root/eventDoc/event/parameterList/parameter/(@module, eventgen:ref-module-lookup(@name, @module)))"/>
  <xsl:for-each select="$moduleNames">
    <xsl:sort select="." />
    <xsl:value-of select="concat('&#10;#include &quot;agent/gen/',
                           lower-case(translate(., '-', '_')),
                           '.h&quot;')"/>
  </xsl:for-each>
whoombat
  • 43
  • 4