How to emulate C enum in XSLT with extra values

I am trying to convert XSLT that generates C code, the following XML should be converted:

<enum name="anenum"> <enumValue name="a"/> <enumValue name="b"/> <enumValue name="c" data="10"/> <enumValue name="d" /> <enumValue name="e" /> </enum> 

It should convert to some C code as follows:

 enum anenum { a = 0, b = 1, c = 10, d = 11, e = 12 } 

or alternatively (since the C preprocessor handles the summation):

  enum anenum { a = 0, b = 1, c = 10, d = c+1, e = c+2 } 

The core of my XSLT looks like this:

 <xsl:for-each select="enumValue"> <xsl:value-of select="name"/> <xsl:text> = </xsl:text> <xsl:choose> <xsl:when test="string-length(@data)&gt;0"> <xsl:value-of select="@data"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="position()-1"/> </xsl:otherwise> </xsl:choose> <xsl:text>, 

(for simplicity, I skip the "no comma at the last element" code part)

In this example, the correct values ​​for d and e will not be created.

I am trying to get it to work with the variable d and e , but so far I have not been successful.

The use of structures such as:

 <xsl:when test="string-length(preceding-sibling::enumValue[1]/@datavalue)&gt;0"> <xsl:value-of select="preceding-sibling::enumValue/@data + 1"/> </xsl:when> 

... only works for the first after the specified value (in this case, d ).

Who can help me? I probably think too much in procedural form ...

+4
source share
4 answers

Non-recursive solution using keys :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*"/> <xsl:output method="text"/> <xsl:key name="koffsetEnums" match="enumValue[@data]" use="generate-id()"/> <xsl:template match="enum"> enum <xsl:value-of select="@name"/> { <xsl:apply-templates select="enumValue"/> } </xsl:template> <xsl:template match="enumValue"> <xsl:value-of select="concat(@name, ' = ')"/> <xsl:variable name="voffsetValueId" select= "generate-id((. | preceding-sibling::enumValue) [@data][last()] )"/> <xsl:choose> <xsl:when test="not($voffsetValueId)"> <xsl:value-of select="concat(position(),'&#xA; ')"/> </xsl:when> <xsl:otherwise> <xsl:variable name="vinitOffset" select= "key('koffsetEnums', $voffsetValueId)/@data" /> <xsl:value-of select= "$vinitOffset + count(preceding-sibling::enumValue) - count(key('koffsetEnums', $voffsetValueId)/preceding-sibling::enumValue) " /> <xsl:text>&#xA; </xsl:text> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> 

When the above conversion applies to the originally provided XML document :

 <enum name="anenum"> <enumValue name="a"/> <enumValue name="b"/> <enumValue name="c" data="10"/> <enumValue name="d" /> <enumValue name="e" /> </enum> 

the required result is created :

 enum anenum { a = 1 b = 2 c = 10 d = 11 e = 12 } 
+3
source

The best solution with keys, avoiding most of the use of the axis of the previous child node :

This conversion is :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*"/> <xsl:output method="text"/> <!-- --> <xsl:key name="ksimpleEnValues" match="enumValue[not(@data)]" use="generate-id(preceding-sibling::enumValue[@data][1])"/> <!-- --> <xsl:template match="enum"> enum <xsl:value-of select="@name"/> { <xsl:apply-templates select= "key('ksimpleEnValues', '') "/> <xsl:apply-templates select="enumValue[@data]"/> } </xsl:template> <!-- --> <xsl:template match="enumValue"> <xsl:param name="pOffset" select="0"/> <xsl:value-of select= "concat(@name, ' = ', position()+$pOffset,'&#xA; ')"/> </xsl:template> <!-- --> <xsl:template match="enumValue[@data]"> <xsl:value-of select= "concat(@name, ' = ', @data,'&#xA; ')"/> <!-- --> <xsl:apply-templates select= "key('ksimpleEnValues', generate-id())"> <xsl:with-param name="pOffset" select="@data"/> </xsl:apply-templates> </xsl:template> </xsl:stylesheet> 

when applied to the original XML document :

 <enum name="anenum"> <enumValue name="a"/> <enumValue name="b"/> <enumValue name="c" data="10"/> <enumValue name="d" /> <enumValue name="e" /> </enum> 

It produces the desired result :

 enum anenum { a = 1 b = 2 c = 10 d = 11 e = 12 } 

Explanation

  • A key named ksimpleEnValues indexes all enumValue elements that do not have a data attribute. Indexing is performed using the generate-id () value of the first preceding enumValue element that has the data attribute.

  • Thus, key('ksimpleEnValues', someId) is a collection of nodes containing all enumValue elements following enumValue , which has its generate-id () equal to someId , and all these enumValue elements precede the next enumValue with data attribute if such exists.

  • key ('ksimpleEnValues', '') is the node-set of all enumValue elements that do not have a previous enumValue element with the data attribute.

  • The template that matches enumValue takes an optional parameter $pOffset , in which the value of the data attribute from the immediately preceding enumValue element with this attribute will be passed, otherwise the default value for $pOffset is 0.

  • The enumValue elements matching the template that have the data attribute call its enum value (@name = @data), and then apply the templates to all enumValue elements between themselves and the next (if any) enumValue with the data attribute. The value of the data attribute is passed as the parameter $pOffset , and it will be added to the relative position of each selected enumValue element when creating output from its processing.

+2
source

You cannot change the "variables" in xsl, but you can use recursion. Do not use the predicates of your previous brother unless they are absolutely urgent, as they will kill your productivity.

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:template match="/" > <xsl:call-template name="printEnum"> <xsl:with-param name="value" select="0"/> <xsl:with-param name="position" select="1"/> </xsl:call-template> </xsl:template> <xsl:template name="printEnum"> <xsl:param name="position"/> <xsl:param name="value" select="0"/> <xsl:variable name="node" select="/enum/enumValue[$position]"/> <xsl:variable name="enumValue"> <xsl:choose> <xsl:when test="$node/@data"> <xsl:value-of select="$node/@data"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$value + 1"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="concat($node/@name, ' = ', $enumValue, ' , ')"/> <xsl:if test="/enum/enumValue[$position + 1]"> <xsl:call-template name="printEnum"> <xsl:with-param name="value" select="$enumValue"/> <xsl:with-param name="position" select="$position + 1"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet> 
+1
source

You are pretty close, I think, but you need the data value of the first preceding enumValue sibling with the data attribute, not the data value of the first preceding enumValue. Then add the number of previous enibValue siblings of the current node and subtract the number of previous enumValue members from the node from which you took the data value.

0
source

All Articles