Recipe12.4.Generating Data Wrappers


Recipe 12.4. Generating Data Wrappers

Problem

You want to create classes that wrap the data contained in each message with a type-safe interface.

Solution

The solution works in two modes. If a message name is provided in a parameter, then it generates a wrapper only for that message data. Otherwise, if no message is specified, it generates wrappers for all messages:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">       <xsl:output method="text"/>   <xsl:strip-space elements="*"/>       <!--The message to generate data for. '*' for all -->   <xsl:param name="message" select=" '*' "/>   <!--The directory to generate code -->   <xsl:param name="generationDir" select=" 'src/' "/>   <!--The C++ header extension to use -->   <xsl:param name="headerExt" select=" '.h' "/>   <!--The C++ source extension to use -->   <xsl:param name="sourceExt" select=" '.C' "/>       <!--Key to locate data types by name -->   <xsl:key name="dataTypes" match="Structure" use="Name" />    <xsl:key name="dataTypes" match="Primitive" use="Name" />    <xsl:key name="dataTypes" match="Array" use="Name" />    <xsl:key name="dataTypes" match="Enumeration" use="Name" />       <!-- Top-level template determines which messages to process -->   <xsl:template match="/">     <xsl:choose>         <xsl:when test="$message = '*'">           <xsl:apply-templates select="*/Messages/*"/>         </xsl:when>         <xsl:when test="*/Messages/Message[Name=$message]">           <xsl:apply-templates select="*/Messages/Message[Name=$message]"/>         </xsl:when>         <xsl:otherwise>           <xsl:message terminate="yes">No such message name            [<xsl:value-of select="$message"/>]</xsl:message>         </xsl:otherwise>       </xsl:choose>   </xsl:template>     <!-- If the messages data type is contained in the repository, then generate data  wrapper header and source file for it --> <xsl:template match="Message">   <xsl:choose>     <xsl:when test="key('dataTypes',DataTypeName)">       <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="header"/>       <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="source"/>     </xsl:when>     <xsl:otherwise>             <xsl:message>Message name [<xsl:value-of select="Name"/>] uses data             [<xsl:value-of select="DataTypeName"/>] that is not defined in the              repository.</xsl:message>     </xsl:otherwise>   </xsl:choose> </xsl:template>          <!-- We only generate headers if a messages data type is a Structure.  The only other typical message data type is XML. We don't generate wrappers for XML  payloads.--> <xsl:template match="Structure" mode="header"> <xsl:document href="{concat($generationDir,Name,$headerExt)}"> #include &lt;primitives/primitives.h&gt;     class <xsl:value-of select="Name"/> { public:<xsl:text>&#xa;&#xa;</xsl:text>   <xsl:for-each select="Members/Member">     <xsl:text>    </xsl:text>     <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="returnType"/>     get_<xsl:value-of select="Name"/>( ) const ;<xsl:text/>     <xsl:text>&#xa;</xsl:text>    </xsl:for-each> <xsl:text>&#xa;</xsl:text> private:<xsl:text>&#xa;&#xa;</xsl:text>   <xsl:for-each select="Members/Member">     <xsl:text>    </xsl:text>     <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="data"/>  m_ <xsl:value-of select="Name"/> ;<xsl:text/>     <xsl:text>&#xa;</xsl:text>   </xsl:for-each> } ; </xsl:document> </xsl:template>     <!-- We only generate source if a messages data type is a Structure. --> <!-- The only other typical message data type is XML. We don't          --> <!-- generate wrappers for XML payloads.                                          --> <xsl:template match="Structure" mode="source"> <xsl:document href="{concat($generationDir,Name,$sourceExt)}"> #include "<xsl:value-of select="Name"/><xsl:value-of select="$headerExt"/>"     <xsl:text/>       <xsl:for-each select="Members/Member">     <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="returnType"/>     <xsl:text>  </xsl:text>     <xsl:value-of select="../../Name"/>::get_<xsl:value-of select="Name"/>( ) const     <xsl:text>&#xa;</xsl:text>     <xsl:text>{&#xa;</xsl:text>     <xsl:text>    return m_</xsl:text><xsl:value-of select="Name"/>     <xsl:text>;&#xa;</xsl:text>     <xsl:text>}&#xa;&#xa;</xsl:text>    </xsl:for-each>     </xsl:document> </xsl:template>     <!-- We assume members that are themselves structures are --> <!-- returned by reference. --> <xsl:template match="Structure" mode="returnType"> const <xsl:value-of select="Name"/>&amp;<xsl:text/> </xsl:template>     <!-- We map primitives that can be represented by native C++ types to those native  types. --> <!-- Otherwise we assume the primitive is externally defined. --> <xsl:template match="Primitive" mode="returnType">   <xsl:choose>     <xsl:when test="Name='Integer' ">int</xsl:when>     <xsl:when test="Name='Real' ">double</xsl:when>     <xsl:otherwise><xsl:value-of select="Name"/></xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template match="*" mode="returnType"> <xsl:value-of select="Name"/> </xsl:template>     <xsl:template match="Primitive" mode="data">   <xsl:choose>     <xsl:when test="Name='Integer' ">int</xsl:when>     <xsl:when test="Name='Real' ">double</xsl:when>     <xsl:otherwise><xsl:value-of select="Name"/></xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template match="*" mode="data"> <xsl:value-of select="Name"/> </xsl:template>     </xsl:stylesheet>

This generator produces only a get interface, but you can easily extend it to generate set functions or other types of functions. Here is a sample generated header file:

#include <primitives/primitives.h>     class AddStockOrderData { public:         StkSymbol get_symbol( ) const ;     Shares get_quantity( ) const ;     BuyOrSell get_side( ) const ;     OrderType get_type( ) const ;     double get_price( ) const ;         private:         StkSymbol  m_symbol ;     Shares  m_quantity ;     BuyOrSell  m_side ;     OrderType  m_type ;     double  m_price ;     } ;

Here is a sample cpp file:

#include "AddStockOrderData.h"     StkSymbol  AddStockOrderData::get_symbol( ) const {     return m_symbol; }     Shares  AddStockOrderData::get_quantity( ) const {     return m_quantity; }     BuyOrSell  AddStockOrderData::get_side( ) const {     return m_side; }     OrderType  AddStockOrderData::get_type( ) const {     return m_type; }     double  AddStockOrderData::get_price( ) const {     return m_price; }

Discussion

This section uses the term wrapper to denote a class that provides an object-oriented interface to data that is otherwise just a plain old C struct. I once worked on a project that hand-coded all our message wrappers. Although the work was tedious, the result was well worth the effort. Consider a message that contains prices, quantities, and dates. An integer type might encode both of these higher-level types. You could easily make a mistake and substitute one for the other without the compiler noticing. Wrappers provide a way to put a skin around your message data that converts low-level representations to class-based primitives such as Price, Qty, and Date. An autogenerated wrapper provides this benefit with less effort.

A message repository and XSLT-based generator allow you to automate the task of producing wrappers. In practice, wrappers sometimes contain some smarts, and you might need to store additional metadata in the repository to get corresponding code-generation smarts. One common case occurs when message data contains arrays. Often another field is present that states how many items are actually stored in the array. If you hand-coded a wrapper function to add an item to this array, it would need to reference this field to find the next empty locations and increment it after adding the new data. You could generate such code only if the repository associated the array size field with the array field.




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