Recipe 12.1. Generating Constant DefinitionsProblemYou want to generate a source file containing all message names as constant equivalent to their message IDs. SolutionYou 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 */ DiscussionTo 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:
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> |