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>
</xsl:text>
<xsl:text>#define </xsl:text>
<xsl:value-of select="$guard"/>
<xsl:text>


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


#endif /* </xsl:text>
<xsl:value-of select="$guard"/>
<xsl:text> */
</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)<40">
<xsl:value-of select="$text"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($text,1,39)"/>
<xsl:text>*
</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>
</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 
</xsl:text>
<xsl:text>{
</xsl:text>
</xsl:template>
<xsl:template name="constants-end">
<xsl:text>

}
</xsl:text>
</xsl:template>
</xsl:stylesheet>
|