Konstanten-Definitionen erzeugen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie möchten eine Quelldatei generieren, die alle Nachrichtennamen als Konstanten enthält, die äquivalent zu ihren Nachrichten-IDs sind.

Lösung

Sie können eine einzelne Transformation konstruieren, die C++ als vorgegebenes Ziel benutzt, aber leicht auch an C, C# oder Java angepasst werden kann:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>
  <!-- Der Name der Ausgabe-Quellcodedatei. -->
  <xsl:param name="file" select=" 'MESSAGE_IDS.h' "/>
  <!-- Standardmäßig werden Konstanten im C++-Stil generiert -->
  <xsl:variable name="constants-type" select=" 'const int' "/>
  <!-- Der normale C++-Zuweisungsoperator -->
  <xsl:variable name="assignment" select=" ' = ' "/>
  <!-- Das normale C++-Anweisungsterminierungszeichen -->
  <xsl:variable name="terminator" select=" ';' "/>
  <!-- Transformiere Repository in eine Sequenz von Konstanten für die Nachrichtendefinitionen -->
  <xsl:template match="MessageRepository">
    <xsl:call-template name="constants-start"/>
    <xsl:apply-templates select="Messages/Message"/>
    <xsl:call-template name="constants-end"/>
  </xsl:template>
  <!-- Aus jeder Nachricht wird ein Kommentar und eine Konstantendefinition -->
  <xsl:template match="Message">
    <xsl:apply-templates select="." mode="doc" />
    <xsl:apply-templates select="." mode="constant" />
  </xsl:template>
  <!-- C++-Headerdateien beginnen mit einem Include-Wächter -->
  <xsl:template name="constants-start">
    <xsl:variable name="guard" select="translate($file,'.','_')"/>
    <xsl:text>#ifndef </xsl:text>
    <xsl:value-of select="$guard"/>
    <xsl:text>&#xa;</xsl:text>
    <xsl:text>#define </xsl:text>
    <xsl:value-of select="$guard"/>
    <xsl:text>&#xa;&#xa;&#xa;</xsl:text>
  </xsl:template>
  <!-- C++-Headerdateien enden mit dem Abschluss des obersten Include-Wächter -->
  <xsl:template name="constants-end">
    <xsl:variable name="guard" select="translate($file,'.','_')"/>
    <xsl:text>&#xa;&#xa;&#xa;#endif /* </xsl:text>
    <xsl:value-of select="$guard"/>
    <xsl:text> */&#xa;</xsl:text>
  </xsl:template>
  <!-- Jeder Konstantendefinition geht ein Kommentar voraus, der die zugehörige Nachricht beschreibt. -->
  <xsl:template match="Message" mode="doc">
  /*
   * Zweck: <xsl:call-template name="format-comment"><xsl:with-param name="text" select="Documentation"/></xsl:call-template>
   * Datenformat: <xsl:value-of select="DataTypeName"/>
   * Von: <xsl:apply-templates select="Senders" mode="doc"/>
   * An: <xsl:apply-templates select="Receivers" mode="doc"/>
   */
  </xsl:template>
  <!-- Wird bei der Generierung von Nachrichtendokumentation benutzt. Listet Sender- oder Empfängerprozesse auf. -->
  <xsl:template match="Senders|Receivers" mode="doc">
    <xsl:for-each select="ProcessRef">
      <xsl:value-of select="."/>
      <xsl:if test="position( ) != last( )">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
  <!-- Dieses Hilfsmittel umbricht Kommentare bei 40 Zeichen -->
  <xsl:template name="format-comment">
    <xsl:param name="text"/>
    <xsl:choose>
      <xsl:when test="string-length($text)&lt;40">
        <xsl:value-of select="$text"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="substring($text,1,39)"/>
        <xsl:text>*&#xa;</xsl:text>
        <xsl:call-template name="format-comment">
          <xsl:with-param name="text" select="substring($text,40)"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <!-- Aus jedem Nachrichtennamen wird eine Konstante, deren Wert die Nachrichten-ID ist. -->
  <xsl:template match="Message" mode="constant">
    <xsl:value-of select="$constants-type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="Name"/>
    <xsl:value-of select="$assignment"/>
    <xsl:value-of select="MsgId"/>
    <xsl:value-of select="$terminator"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:template>
  <!-- Ignoriere Textknoten, die nicht explizit von den obigen Templates behandelt werden. -->
  <xsl:template match="text( )"/>
</xsl:stylesheet>










Wenn sie auf Ihrem Repository ausgeführt wird, generiert diese Transformation den folgenden Code:

#ifndef MESSAGE_IDS_h
#define MESSAGE_IDS_h


  /*
  * Zweck:       Hinzufügen einer neuen Order.
  * Datenformat: AddStockOrderData
  * Von:         StockClient
  * An:          StockServer
  */
  const int ADD_STOCK_ORDER_ID = 1;

  /*
  * Zweck:       Bestätigung der Order-Buchung.
  * Datenformat: AddStockOrderAckData
  * Von:         StockServer
  * An:          StockClient
  */
  const int ADD_STOCK_ORDER_ACK_ID = 2;
  /*
  * Zweck:       Fehler bei der Order-Buchung. Vielleicht
  *              verletzt sie eine Regel.
  * Datenformat: AddStockOrderNackData
  * Von:         StockServer
  * An:          StockClient
  */
  const int ADD_STOCK_ORDER_NACK_ID = 3;

// usw ...

#endif /* MESSAGE_IDS_h */

Diskussion

Um die Transformation für die Code-Generierung mit mehreren Sprachen einsetzen zu können, verwende ich ein Stylesheet, das komplizierter ist, als es für eine Sprache allein notwendig wäre. Und trotzdem wurde es in diesem Kapitel nicht vollständig verallgemeinert. So setzen z.B. die Kommentarkonventionen voraus, dass es sich um eine C-ähnliche Sprache handelt. Außerdem mögen die Kommentare selbst nicht Ihrem persönlichen Stil oder Geschmack entsprechen. Aber wenn Sie eigene Templates für die Code-Generierung erzeugen, sollten Sie ohnehin die folgenden Anpassungen vornehmen:

  • Kodieren Sie spracheigene Konstrukte in Parameterrn oder Variablen auf oberster Ebene, damit sie von importierten Stylesheets oder (falls Sie Parameter verwenden) durch die Übergabe von Parameterwerten bei der Ausführung des Stylesheets überschrieben werden können.
  • Brechen Sie die verschiedenen generierten Komponenten in separate Templates auf, die individuell durch importierte Stylesheets überschrieben werden können.

Wenn Sie ihre Transformation auf diese Weise entwerfen, können Sie mit nur kleinen Änderungen C-artige #define-Konstanten erzeugen:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="msgIds.xslt"/>
  <xsl:variable name="constants-type" select=" '#define ' "/>
  <xsl:variable name="assignment" select=" '   ' "/>
  <xsl:variable name="terminator" select=" '' "/>
</xsl:stylesheet>

Java verlangt, dass sich alles innerhalb einer Klasse befindet, aber auch das können Sie erreichen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="msgIds.xslt"/>
  <xsl:variable name="constants-type" select=" 'public static final int' "/>
  <xsl:template name="constants-start">
    <xsl:text>final public class MESSAGE_IDS &#xa;</xsl:text>
    <xsl:text>{&#xa;</xsl:text>
  </xsl:template>
  <xsl:template name="constants-end">
    <xsl:text>&#xa;&#xa;}&#xa;</xsl:text>
  </xsl:template>
</xsl:stylesheet>

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de