Как эмулировать перечисление C в XSLT с дополнительными значениями - PullRequest
5 голосов
/ 11 февраля 2009

Я пытаюсь сделать XSLT-преобразование, которое генерирует код C, следующий XML должен быть преобразован:

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

Он должен преобразоваться в некоторый код C следующим образом:

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

или альтернативно (поскольку препроцессор C будет обрабатывать суммирование):

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

Ядро моего XSLT выглядит так:

<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>,

(для простоты я пропускаю часть кода «без запятой в последнем элементе»)

В этом примере не будут сгенерированы правильные значения для d и e

Я пытался заставить его работать с переменной d и e , но пока что мне не удается.

Использование таких конструкций, как:

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

... работает только для первого после указанного значения (в данном случае d ).

Кто может мне помочь? Я, наверное, слишком много думаю в процедурном плане ...

Ответы [ 4 ]

4 голосов
/ 11 февраля 2009

Нерекурсивное решение с использованием ключей :

<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>

Когда вышеуказанное преобразование применяется к первоначально предоставленному документу XML :

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

необходим требуемый результат :

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

      }
2 голосов
/ 12 февраля 2009

Лучшее решение с ключами, позволяющее избежать наибольшего использования оси предыдущего брата :

Это преобразование :

<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>

при применении к первоначально предоставленному документу XML :

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

Дает желаемый результат :

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

Объяснение

  1. Ключ с именем ksimpleEnValues индексирует все элементы enumValue, которые не имеют атрибута data. Индексация выполняется по значению generate-id () первого предшествующего элемента enumValue, который имеет атрибут data.

  2. Таким образом, key('ksimpleEnValues', someId) - это набор узлов, содержащий все элементы enumValue, следующие за enumValue, у которого его generate-id () равен someId, а все эти enumValue элементы предшествуют следующему enumValue с атрибутом data, если таковой существует.

  3. key ('ksimpleEnValues', '') - это набор узлов всех enumValue элементов, которые не имеют предшествующего элемента enumValue с атрибутом data.

  4. Шаблон, соответствующий enumValue, принимает необязательный параметр $pOffset, в котором будет передано значение атрибута data из непосредственно предшествующего элемента enumValue с этим атрибутом, в противном случае по умолчанию значение для $pOffset равно 0.

  5. Шаблон, соответствующий элементам enumValue, имеющим атрибут data, создает свое перечисляемое значение (@name = @data) и затем применяет шаблоны ко всем элементам enumValue между собой и следующим (если таковой имеется) существует) enumValue с атрибутом data. Значение атрибута data передается как параметр $pOffset, и оно будет добавлено к относительной позиции каждого выбранного элемента enumValue при создании вывода его обработки.

1 голос
/ 11 февраля 2009

Вы не можете изменить «переменные» в xsl, но вы можете использовать рекурсию. Не используйте предикаты предшествующих братьев и сестер, если они не являются абсолютно срочными, поскольку они убьют вашу производительность.

<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>
0 голосов
/ 11 февраля 2009

Я думаю, вы достаточно близки, но вам нужно значение данных первого предшествующего элемента enumValue с атрибутом данных, а не значение данных первого предшествующего enumValue. Затем добавьте число предшествующих братьев и сестер enumValue текущего узла и вычтите число предшествующих братьев и сестер enumValue узла, из которого вы взяли значение данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...