Recipe12.1.Generating Constant Definitions


Recipe 12.1. Generating Constant Definitions

Problem

You want to generate a source file containing all message names as constant equivalent to their message IDs.

Solution

You can construct a single transformation that uses C++ as the default target but is easily customized for C, C#, or Java:

<?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="*"/>       <!--The name of the output source code file. -->    <xsl:param name="file" select=" 'MESSAGE_IDS.h' "/>      <!-- The default behavior is to generate C++ style constants -->   <xsl:variable name="constants-type" select=" 'const int' "/>       <!-- The default C++ assignment operator -->   <xsl:variable name="assignment" select=" ' = ' "/>       <!-- The default C++ statement terminator -->   <xsl:variable name="terminator" select=" ';' "/>           <!--Transform repository into a sequence of message constant        definitions -->     <xsl:template match="MessageRepository">     <xsl:call-template name="constants-start"/>     <xsl:apply-templates select="Messages/Message"/>     <xsl:call-template name="constants-end"/>   </xsl:template>         <!--Each meesage becomes a comment and a constant definition -->   <xsl:template match="Message">     <xsl:apply-templates select="." mode="doc" />     <xsl:apply-templates select="." mode="constant" />   </xsl:template>       <!-- C++ header files start with an inclusion guard -->   <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++ header files end with the closure of the top-level inclusion         guard -->   <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>       <!-- Each constant definition is preceded by a comment describing the         associated message -->   <xsl:template match="Message" mode="doc">   /*   * Purpose:      <xsl:call-template name="format-comment">                          <xsl:with-param name="text" select="Documentation"/>                         </xsl:call-template>   * Data Format: <xsl:value-of select="DataTypeName"/>   * From:        <xsl:apply-templates select="Senders" mode="doc"/>   * To:          <xsl:apply-templates select="Receivers" mode="doc"/>   */   </xsl:template>       <!-- Used in the generation of message documentation. Lists sender or        receiver processes -->   <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>       <!-- This utility wraps comments at 40 characters wide -->   <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>       <!-- Each message name becomes a constant whose value is the message         id -->   <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>      <!-- Ignore text nodes not explicitly handled by above templates -->   <xsl:template match="text( )"/>    </xsl:stylesheet>

When run against your repository, this transform generates the following code:

#ifndef MESSAGE_IDS_h #define MESSAGE_IDS_h           /*   * Purpose:     Add a new order.   * Data Format: AddStockOrderData   * From:        StockClient   * To:          StockServer   */   const int ADD_STOCK_ORDER_ID = 1;       /*   * Purpose:     Acknowledge the order has been added.   * Data Format: AddStockOrderAckData   * From:        StockServer   * To:          StockClient   */   const int ADD_STOCK_ORDER_ACK_ID = 2;       /*   * Purpose:     Error adding the order. Perhaps it violates   *              a rule.   * Data Format: AddStockOrderNackData   * From:        StockServer   * To:          StockClient   */   const int ADD_STOCK_ORDER_NACK_ID = 3;     //Etc ...     #endif /* MESSAGE_IDS_h */

Discussion

To make the code-generation transformation customizable for several languages, I use a stylesheet that is more complex than necessary for any single language. Still, this chapter did not generalize it completely. For example, the commenting conventions assume the language is in the C ancestry. The content of the comments also may not suit your particular style or taste. However, as you create your own code-generation templates, you should apply these customization techniques:

  • Encode language-specific constructs in top-level parameters or variables so they can be overridden by importing stylesheets or (if you use parameters) by passing in parameter values when the stylesheet is run.

  • Break the various generated components into separate templates that can be overridden individually by importing stylesheets.

Having designed the transformation in this way allows C-style #define constants to be generated with only minor changes:

<?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 requires everything to live inside a class, but you can accommodate that too:

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




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net