Chapter 12. Code Generation

Good programmers write good code. Great programmers write programs to generate it.

Unknown

Introduction

Automation is the holy grail of software development. In fact, much of the progress in software development is driven by the notion of code generation from some higher-level specification. After all, isn’t that what assemblers and compilers do? However, in another form of code generation, the target language is not executable machine code, but a high-level language such as Java or C++. Why would you want to generate code in this way, and what does XML have to do with it?

When you write programs, you essentially encode many kinds of knowledge into a very specific syntax that is optimized for one particular development life-cycle phase. It is difficult to leverage the work done in coding to other important development tasks because programming languages are difficult to parse and much of the interesting information is encoded in ad-hoc comments. Representing application knowledge in XML provides the opportunity for much greater leverage. From XML specifications, you can generate application code, test programs, documentation, and possibly even test data. This is not to say that XML gives you this for free. As with all software-development tasks, a great deal of planning and infrastructure building is required to reap the benefits.

This chapter is different from most other chapters in this book because most examples are components of a solution within the context of a particular application. The reason for this structure is two-fold.

First, it is unlikely that you would encode information in XML to generate code just because XML is cool. In most cases, a larger problem must be solved in which XML can be further leveraged. The examples in this section will make more sense if they are presented in the context of a larger problem.

Second, the particular problem is common in large-scale application development, so readers might find it interesting in its own right. However, even if this is not the case, the larger problem will not take away from the application of the concepts to other development tasks.

So what is this large problem?

Imagine a complex client-server application. Complex means that it consists of many types of server and client processes. These processes communicate via messages using message-oriented middleware (either pointing to point, publish/subscribe, or both). IBM MQSeries, Microsoft Message Queuing (MSMQ), BEA Systems Tuxedo, and TIBCO Rendezvous are just a few of the many products in this space. In this example, the particular middleware product is not particularly relevant. What is relevant is that all significant work performed by the system is triggered by the receipt of a message and the subsequent response involving one or more messages.[1] The message may contain XML (SOAP), non-XML text, or binary data. Chapter 12 covers SOAP in the context of WSDL. This chapter is primarily interested in server-to-server communication in which XML is used less often.

What is particularly daunting about such complex systems is that you cannot simply understand them by viewing the source code of any one particular type of process. You must begin by first understanding the conversations or inter-process messaging protocols spoken by these processes. This chapter goes even further and states that, at a first level of approximation, the details of each individual process are irrelevant. You can simply treat each process as a black box. Then, rather than understand the hundreds of thousands of lines of code that make up the entire system, you can start by understanding the smaller set of messages that these processes exchange.

Thus the question becomes, how do you go about understanding the interprocess language of a complex application? Can you go to a single place to get this information? Sadly, this is often not the case. I find that you can rarely find an up-to-date and complete specification of an application’s messaging protocols. You can usually find pieces of the puzzle in various shared header files and other pieces in design documents developed over the system’s life cycle, but rarely will you find a one-stop source for such vital information. And in many cases, the only truly reliable method of obtaining such information is to reverse-engineer it from the applications’ source code, which is exactly what I claimed you should not have to do!

Okay, so what does this problem have to do with XML, XSLT, and, in particular, code generation? You can describe the solution to this problem in terms of the need for a documentation that describes in complete detail an application’s interprocess messaging structure. What kind of document should this be? Maybe the developers should maintain an MS Word document describing all the messages or, better still, a messaging web site that can be browsed and searched. Or, maybe (and you should have guessed the answer already) the information should be kept in XML! Perhaps you should generate the web site from this XML. While you’re at it, maybe you should generate some of the code needed by the applications that processes these messages. This is, in fact, exactly what you shall do in this chapter. I call the set of XML files an interprocess message repository . Many recipes in this chapter demonstrate how to generate code using this repository.

Before moving to the actual recipes, this chapter presents the repository’s design in terms of its schema. It uses W3C XSD Schema for this purpose but only shows an intuitive graphical view for those unfamiliar with XML schema.

Figure 12-1 was produced using Altova’s XML Spy 4.0 (http://www.xmlspy.com). The icons with three dots (...) represent an ordered sequence. The icon that looks like a multiway switch represents a choice.

Graphical representation of XSD schema for repository
Figure 12-1. Graphical representation of XSD schema for repository

Although this schema is sufficient to illustrate interesting code-generation recipes, it is probably inadequate for an industrial-strength message repository. Additional data might be stored in a message repository, as shown in the following list:

  • Symbolic constants used in array and string sizes, as well as enumerated values

  • Information about more complex data representations, such as unions and type-name aliases (C typedefs)

  • Information about message protocols (complex sequences of messages exchanged by sets of processes to achieve a specific functionality)

  • Historical information such as authors, last changed by, change dates, etc.

  • Delivery and transport information related to publishers and subscribers or queue names

As sample repository data, imagine a simple client-server application that submits orders and cancellations for common stock. The repository for such an application might look like this:

<MessageRepository xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:
noNamespaceSchemaLocation="C:MyProjectsXSLT Cookbookcode genMessageRepository.
xsd">
  <DataTypes>
    <Primitive>
      <Name>Real</Name>
      <Size>8</Size>
      <Category>real</Category>
    </Primitive>
    <Primitive>
      <Name>Integer</Name>
      <Size>4</Size>
      <Category>signed integer</Category>
    </Primitive>
    <Primitive>
      <Name>StkSymbol</Name>
      <Size>10</Size>
      <Category>string</Category>
    </Primitive>
    <Primitive>
      <Name>Message</Name>
      <Size>100</Size>
      <Category>string</Category>
    </Primitive>
    <Primitive>
      <Name>Shares</Name>
      <Size>4</Size>
      <Category>signed integer</Category>
    </Primitive>
    <Enumeration>
      <Name>BuyOrSell</Name>
      <Enumerators>
        <Enumerator>
          <Name>BUY</Name>
          <Value>0</Value>
        </Enumerator>
        <Enumerator>
          <Name>SELL</Name>
          <Value>1</Value>
        </Enumerator>
      </Enumerators>
    </Enumeration>
    <Enumeration>
      <Name>OrderType</Name>
      <Enumerators>
        <Enumerator>
          <Name>MARKET</Name>
          <Value>0</Value>
        </Enumerator>
        <Enumerator>
          <Name>LIMIT</Name>
          <Value>1</Value>
        </Enumerator>
      </Enumerators>
    </Enumeration>
    <Structure>
      <Name>TestData</Name>
      <Members>
        <Member>
          <Name>order</Name>
          <DataTypeName>AddStockOrderData</DataTypeName>
        </Member>
        <Member>
          <Name>cancel</Name>
          <DataTypeName>CancelStockOrderData</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>AddStockOrderData</Name>
      <Documentation>A request to add a new order.</Documentation>
      <Members>
        <Member>
          <Name>symbol</Name>
          <DataTypeName>StkSymbol</DataTypeName>
        </Member>
        <Member>
          <Name>quantity</Name>
          <DataTypeName>Shares</DataTypeName>
        </Member>
        <Member>
          <Name>side</Name>
          <DataTypeName>BuyOrSell</DataTypeName>
        </Member>
        <Member>
          <Name>type</Name>
          <DataTypeName>OrderType</DataTypeName>
        </Member>
        <Member>
          <Name>price</Name>
          <DataTypeName>Real</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>AddStockOrderAckData</Name>
      <Documentation>A positive acknowledgment that order was added successfully.
      </Documentation>
      <Members>
        <Member>
          <Name>orderId</Name>
          <DataTypeName>Integer</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>AddStockOrderNackData</Name>
      <Documentation>A negative acknowledgment that order add was unsuccessful.
      </Documentation>
      <Members>
        <Member>
          <Name>reason</Name>
          <DataTypeName>Message</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>CancelStockOrderData</Name>
      <Documentation>A request to cancel all or part of an order</Documentation>
      <Members>
        <Member>
          <Name>orderId</Name>
          <DataTypeName>Integer</DataTypeName>
        </Member>
        <Member>
          <Name>quantity</Name>
          <DataTypeName>Shares</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>CancelStockOrderAckData</Name>
      <Documentation>A positive acknowledgment that order was canceled successfully.
      </Documentation>
      <Members>
        <Member>
          <Name>orderId</Name>
          <DataTypeName>Integer</DataTypeName>
        </Member>
        <Member>
          <Name>quantityRemaining</Name>
          <DataTypeName>Shares</DataTypeName>
        </Member>
      </Members>
    </Structure>
    <Structure>
      <Name>CancelStockOrderNackData</Name>
      <Documentation>A negative acknowledgment that the order cancel was
      unsuccessful.</Documentation>
      <Members>
        <Member>
          <Name>orderId</Name>
          <DataTypeName>Integer</DataTypeName>
        </Member>
        <Member>
          <Name>reason</Name>
          <DataTypeName>Message</DataTypeName>
        </Member>
      </Members>
    </Structure>
  </DataTypes>
  <Messages>
    <Message>
      <Name>ADD_STOCK_ORDER</Name>
      <MsgId>1</MsgId>
      <DataTypeName>AddStockOrderData</DataTypeName>
      <Senders>
        <ProcessRef>StockClient</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockServer</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>ADD_STOCK_ORDER_ACK</Name>
      <MsgId>2</MsgId>
      <DataTypeName>AddStockOrderAckData</DataTypeName>
      <Senders>
        <ProcessRef>StockServer</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockClient</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>ADD_STOCK_ORDER_NACK</Name>
      <MsgId>3</MsgId>
      <DataTypeName>AddStockOrderNackData</DataTypeName>
      <Senders>
        <ProcessRef>StockServer</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockClient</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>CANCEL_STOCK_ORDER</Name>
      <MsgId>4</MsgId>
      <DataTypeName>CancelStockOrderData</DataTypeName>
      <Senders>
        <ProcessRef>StockClient</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockServer</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>CANCEL_STOCK_ORDER_ACK</Name>
      <MsgId>5</MsgId>
      <DataTypeName>CancelStockOrderAckData</DataTypeName>
      <Senders>
        <ProcessRef>StockServer</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockClient</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>CANCEL_STOCK_ORDER_NACK</Name>
      <MsgId>6</MsgId>
      <DataTypeName>CancelStockOrderNackData</DataTypeName>
      <Senders>
        <ProcessRef>StockServer</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockClient</ProcessRef>
      </Receivers>
    </Message>
    <Message>
      <Name>TEST</Name>
      <MsgId>7</MsgId>
      <DataTypeName>TestData</DataTypeName>
      <Senders>
        <ProcessRef>StockServer</ProcessRef>
      </Senders>
      <Receivers>
        <ProcessRef>StockClient</ProcessRef>
      </Receivers>
    </Message>
  </Messages>
  <Processes>
    <Process>
      <Name>StockClient</Name>
    </Process>
    <Process>
      <Name>StockServer</Name>
    </Process>
  </Processes>
</MessageRepository>

This repository describes the messages that are sent between a client (called StockClient) and a server (called StockServer) as the application performs its various duties. Readers familiar with WSDL will see a similarity; however, WSDL is specific to web-service specifications and is most often used in the context of SOAP services, even though the WSDL specification is technically protocol-neutral (http://www.w3.org/TR/wsdl).

The last two examples in this chapter are independent of the messaging problem. The first focuses on generating C++ code from Unified Modeling Language (UML) models exported from a UML modeling tool via XML Metadata Interchange (XMI). The second discusses using XSLT to generate XSLT.

Before proceeding with the actual examples, I apologize for favoring C++ for most of the examples. I did this only because it is the language with which I am most familiar; it is the language for which I have actually written generators; and the conceptual framework is transferable to other languages, even if the literal XSLT is not.[2]

XSLT 2.0

Due to the specialized nature of these recipes, I do not present both XSLT 1.0 and 2.0 solutions, as is done in many of the earlier chapters. However, readers interested in using XSLT 2.0 to generate code should study Chapter 6. Many of the 2.0 specific features and techniques presented there can be applied nicely to code generation. Here are a list of general ideas:

  • Take advantage of the new separator attribute in xsl:value-of.

    Generating code often involves generating delimited sequences (for example, comma delimited lists). In these cases, the ability of xsl:value-of to automatically insert a delimiter when outputting a sequence can lead to simpler generators.

  • Exploit XPath 2.0’s powerful sequence constructs.

    One of the biggest annoyances encountered when writing generators in XSLT 1.0 is their verbosity. Generators tend to include a lot of looping and conditional constructs, and xsl:for-each and xsl:choose are not paragons of brevity. However, many generation tasks can be shifted from XSLT to XPath 2.0. Often this will involve using both XSLT and XPath to convert the input XML into one or more sequences and then using XPath to map these sequences into code:

    <!-- A function for generating a C style function declaration 
         using mostly XPath 2.0 -->
    
    <xsl:function name="ckbk:function-decl" as="xs:string*">
      <xsl:param name="name" as="xs:string"/>
      <xsl:param name="returnType" as="xs:string"/>
      <xsl:param name="argNames" as="xs:string*"/>
      <xsl:param name="argTypesPre" as="xs:string*"/>
      <xsl:param name="argTypesPost" as="xs:string*"/>
      <xsl:variable name="c" select="count($argNames)"/>
      <xsl:sequence select="$returnType,
                           $name,
                           '(', 
                           for $i in 1 to $c return ($argTypesPre[$i], $argNames[$i],
                                                      $argTypesPost[$i], 
                                                      if ($i ne $c) then ',' else ''),
                           '),' "/> 
    </xsl:function>
  • Modular, reusable XSLT is easier to craft in 2.0.

    If you are keen on creating generators that work across multiple destination languages, the techniques discussed in Recipes 6.3 and 6.6 are worth mastering.

  • Outputting to multiple documents is standardized in 2.0.

    Authors of generators for languages like C and C++ that use separate header and source files are sure to appreciate the now standardized xsl:result-document for writing stylesheets that create multiple output documents.

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>

12.2. Generating Switching Code

Problem

You want to generate the switching code that will route incoming messages to their message handlers.

Solution

The message repository stores information about which processes receive which messages. Therefore, given a process name, you can generate a message switch that routes inbound messages to handlers:

<?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:param name="process" select=" '*' "/>
   
  <xsl:variable name="message-dir" select=" 'messages' "/>
  <xsl:variable name="directory-sep" select=" '/' "/>
  <xsl:variable name="include-ext" select=" '.h' "/>
  
   
  <xsl:template match="MessageRepository">
    <!-- Generate source file preliminaries -->
    <xsl:call-template name="file-start"/>
    
    <!-- Generate includes for messages this process receives -->
    <xsl:apply-templates select="Messages/
                                  Message[Receivers/
                                          ProcessRef = $process or
                                          $process = '*']" 
                         mode="includes"/>
    
    <!-- Generate message switch preliminaries -->                                
    <xsl:call-template name="switch-start"/>
    
    <!-- Generate switch body -->
    <xsl:apply-templates select="Messages/
                                  Message[Receivers/
                                          ProcessRef = $process or
                                           $process = '*']" 
                         mode="switch"/>
    
    <!-- Generate switch end -->                                
    <xsl:call-template name="switch-end"/>
    
    <!-- Generate file end -->
    <xsl:call-template name="file-end"/>
  </xsl:template>     
   
  <!-- Generate an include for each message -->     
  <xsl:template match="Message" mode="includes">
    <xsl:text>#include &lt;</xsl:text>
    <xsl:value-of select="$message-dir"/>
    <xsl:value-of select="$directory-sep"/>
    <xsl:value-of select="Name"/>
    <xsl:value-of select="$include-ext"/>
    <xsl:text>&gt;&#xa;</xsl:text>
  </xsl:template>
   
  <!-- Generate handler case for each message type -->
  <xsl:template match="Message" mode="switch">
    case <xsl:value-of select="Name"/>_ID:
      <xsl:call-template name="case-action"/>
  </xsl:template>
   
<!-- Generate the message handler action -->
<xsl:template name="case-action">
      return <xsl:value-of select="Name"/>(*static_cast&lt;const <xsl:value-of
      select="DataTypeName"/>*&gt;(msg.getData())).process() ;
</xsl:template>
   
  <!-- Do nothing by default. Users will override if necessary -->
  <xsl:template name="file-start"/>
  <xsl:template name="file-end"/>
   
  <!-- Generate start of switch statement --> 
  <xsl:template name="switch-start">
#include &lt;transport/Message.h&gt;
#include &lt;transport/MESSAGE_IDS.h&gt;
   
<xsl:text>&#xa;&#xa;</xsl:text>
<xsl:call-template name="process-function"/>
{
  switch (msg.getId())
  {
  </xsl:template>
  
  <xsl:template name="switch-end">
    return false ;
  }
}
  </xsl:template>
   
<!-- Generate signature for message-processing entry point -->
<xsl:template name="process-function">
bool processMessage(const Message&amp; msg)
</xsl:template>
   
   
</xsl:stylesheet>

When applied to your test repository, this example produces the following switching code:

#include <messages/ADD_STOCK_ORDER.h>
#include <messages/CANCEL_STOCK_ORDER.h>
   
#include <transport/Message.h>
#include <transport/MESSAGE_IDS.h>
   
bool processMessage(const Message& msg)
   
{
  switch (msg.getId())
  {
  
    case ADD_STOCK_ORDER_ID:
      
      return ADD_STOCK_ORDER(*static_cast<const 
      AddStockOrderData*>(msg.getData())).process() ;
   
    case CANCEL_STOCK_ORDER_ID:
      
      return CANCEL_STOCK_ORDER(*static_cast<const 
      CancelStockOrderData*>(msg.getData())).process() ;
   
    return false ;
  }
}

Discussion

Applications that process messages always have some form of message switch. The structure of these switches can vary somewhat, but they typically must check for some message identifier and route the message to a handler. The identifier can take the form of an integer (as in this case) or a string. In some cases, multipart identifiers consist of a message type and subtype. On the processing side, the handler can take the form of a simple function or an object that is instantiated to process the message. It is important to make even simple code generators modular so that they can be reused in more than one context. At a company where I once worked, a group decided to create a very interesting Perl-based facility that generated C++ code from an XSD schema. It was of sufficient usefulness that other groups wanted to use it. Unfortunately, the developers thought only in terms of their particular needs, and their otherwise impressive solution was not reusable. Writing even basic code generators is a formidable task, and you should try to make them as general as is practical so others will not have to relive your pain.

The particular solution shown provides several hooks for customizing what appears at the start of the generated file, the start of the switching code, the end of the switching code, and the end of the file. This flexibility allows Recipe 12.5 to reuse this generator. Still, it could use further improvement. For example, this generator assumes that a C-switch statement performs message switching. However, some applications may use an if-else style switch, especially if the message IDs are strings. Others may use a table lookup from the message ID to a function pointer.

Can a single generator be made general enough to handle all these possibilities? Maybe, but the result will probably be extremely complex. A better approach would create generation components that could be reused in more complex generation scenarios. Here is what a generic C-switch statement generator might look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xslt [
  <!--Used to control code intenting -->
  <!ENTITY INDENT "    ">
  <!ENTITY INDENT2 "&INDENT;&INDENT;">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
  <xsl:template name="gen-C-Switch">
    <xsl:param name="variable"/>
    <xsl:param name="cases" select="/.."/>
    <xsl:param name="actions" select="/.."/>
    <xsl:param name="default"/>
    <xsl:param name="baseIndent" select="'&INDENT;'"/>
    <xsl:param name="genBreak" select="true()"/>
   
    <xsl:value-of select="$baseIndent"/>
    <xsl:text>switch (</xsl:text>
    <xsl:value-of select="$variable"/>
    <xsl:text>)&#xa;</xsl:text>
    <xsl:value-of select="$baseIndent"/>
    <xsl:text>{&#xa;</xsl:text>
    
    <xsl:for-each select="$cases">
      <xsl:variable name="pos" select="position()"/>
   
      <xsl:value-of select="$baseIndent"/>
      <xsl:text>&INDENT;case </xsl:text>
      <xsl:value-of select="."/>
      <xsl:text>:&#xa;</xsl:text>
      <xsl:call-template name="gen-C-Switch-caseBody">
        <xsl:with-param name="case" select="."/>
        <xsl:with-param name="action" select="$actions[$pos]"/>
        <xsl:with-param name="baseIndent"
                                      select="concat('&INDENT2;',$baseIndent)"/>
      </xsl:call-template>
      <xsl:if test="$genBreak">
        <xsl:value-of select="$baseIndent"/>
        <xsl:text>&INDENT2;break;</xsl:text>
      </xsl:if>      
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
  
    <xsl:if test="$default">
      <xsl:value-of select="$baseIndent"/>
      <xsl:text>&INDENT;default:</xsl:text>
      <xsl:text>:&#xa;</xsl:text>
      <xsl:call-template name="gen-C-Switch-default-caseBody">
        <xsl:with-param name="action" select="$default"/>
        <xsl:with-param name="baseIndent" 
             select="concat('&INDENT2;',$baseIndent)"/>
      </xsl:call-template>
      <xsl:text>&#xa;</xsl:text>
    </xsl:if>
    <xsl:value-of select="$baseIndent"/>
    <xsl:text>}&#xa;</xsl:text>
  </xsl:template>     
   
    
  <!-- This generates a null statement by default. -->
  <!-- Override to generate code for the case --> 
  <xsl:template name="gen-C-Switch-caseBody">
    <xsl:param name="case"/>
    <xsl:param name="action"/>
    <xsl:param name="baseIndent"/>
    
    <xsl:value-of select="$baseIndent"/>
    <xsl:text>;</xsl:text>
  </xsl:template>
   
  <!-- This invokes the regular case body generator. --> 
  <!-- Overide to do something special for the default case. -->
  <xsl:template name="gen-C-Switch-default-caseBody">
    <xsl:param name="action"/>
    <xsl:param name="baseIndent"/>
    
    <xsl:call-template name="gen-C-Switch-caseBody">
      <xsl:with-param name="action" select="$action"/>
      <xsl:with-param name="baseIndent" select="$baseIndent"/>
    </xsl:call-template>
  </xsl:template>
   
</xsl:stylesheet>

Chapter 14 demonstrates techniques that make generators like this even more generic.

12.3. Generating Message-Handling Stub Code

Problem

You want to generate the skeleton of your message handlers.

Solution

The following stylesheet creates a simple skeleton that takes a process name and generates stub code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
  <xsl:output method="text"/>
   
  <!-- Specifies which process to generate handlers for -->
  <xsl:param name="process"/>
  <!-- Specifies which message to generate handlers for. A special value of %ALL% 
       signifies all messages -->
  <xsl:param name="message" select=" '%ALL%' "/>
   
   
  <!-- The directory where -->
  <xsl:variable name="message-dir" select=" 'messages' "/>
  <xsl:variable name="directory-sep" select=" '/' "/>
  <xsl:variable name="include-ext" select=" '.h' "/>
   
  
  <xsl:template match="MessageRepository">
    <xsl:choose>
      <xsl:when test="$message='%ALL%'"  >
          <xsl:apply-templates
               select="Messages/Message[Receivers/ProcessRef = $process]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates 
             select="Messages/Message[Receivers/ProcessRef = $process and 
                                      Name=$message]"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>     
   
  <xsl:template match="Message">
    <xsl:document href="{concat(Name,'.h')}">
      <xsl:call-template name="makeHeader"/>
    </xsl:document>
    <xsl:document href="{concat(Name,'.cpp')}">
      <xsl:call-template name="makeSource"/>
    </xsl:document>
    </xsl:template>
   
  <xsl:template name="makeHeader">
#ifndef <xsl:value-of select="Name"/>_h
#define <xsl:value-of select="Name"/>_h
   
#include &lt;transport/MessageHandler.h&gt;
   
//Forward Declarations
class <xsl:value-of select="DataTypeName"/> ;
/*!TODO:  Insert addition forward declarations here.*/   
   
class <xsl:value-of select="Name"/> : public MessageHandler
{
public:
    <xsl:value-of select="Name"/>(const <xsl:value-of 
        select="DataTypeName"/>&amp; data) ;
    bool process() ;
private:
   
    const <xsl:value-of select="DataTypeName"/>&amp; m_Data ;
} ;
   
#endif
  </xsl:template>  
  
  <xsl:template name="makeSource">
#include &lt;messages/<xsl:value-of select="Name"/>.h&gt;
   
/*!TODO:  Insert addition includes here.*/   
   
<xsl:value-of select="Name"/>::<xsl:value-of select="Name"/>(const 
    <xsl:value-of select="DataTypeName"/>&amp; data)
  : m_Data(data)
{
}
   
bool <xsl:value-of select="Name"/>::process()
{
  /*!TODO:  Insert message handler code here. */
  return true; 
}
  </xsl:template>  
  
</xsl:stylesheet>

This stylesheet generates a header and a source file for each message it processes, as shown in Example 12-1 and Example 12-2.

Example 12-1. AddStockOrder.h
#ifndef ADD_STOCK_ORDER_h
#define ADD_STOCK_ORDER_h
   
#include <transport/MessageHandler.h>
   
//Forward Declarations
class AddStockOrderData ;
/*!TODO:  Insert addition forward declarations here.*/   
   
class ADD_STOCK_ORDER : public MessageHandler
{
public:
    ADD_STOCK_ORDER(const AddStockOrderData& data) ;
    bool process() ;
private:
   
    const AddStockOrderData& m_Data ;
} ;
   
#endif
Example 12-2. AddStockOrder.cpp
#include <messages/ADD_STOCK_ORDER.h>
   
/*!TODO:  Insert addition includes here.*/   
   
ADD_STOCK_ORDER::ADD_STOCK_ORDER(const AddStockOrderData& data)
  : m_Data(data)
{
}
   
bool ADD_STOCK_ORDER::process()
{
  /*!TODO:  Insert message handler code here. */
  return true; 
}

Discussion

Much of what developers do is repetitive in structure but unique in substance. In other words, we write a lot of boilerplate code that gets specialized based on the particular context. Performing any sort of repetitive work leads to boredom; boredom leads to distraction; and distraction results in bugs. Generating the repetitive parts of your code lets you concentrate on the important parts.

Tools that generate code with TODO sections are often called wizards. This message handler wizard is a very basic example of this genre. Some commercially available wizards generate the structure of entire applications. It is not clear whether XSLT can scale to create wizards of that magnitude. However, for simple kinds of stub generators for which the input is XML, XSLT is preferable to other languages, including Perl. Many Perl fanatics disagree with this statement because they view XML processing as just another form of text processing (and we all know Perl is the undisputed king of text-processing languages). However, a strong argument can be made that XML processing is not text processing, but the processing of trees that contain text nodes. As such, XSLT facilities transform trees more nimbly than Perl does. Nevertheless, Perl and XSLT can team up to create a best-of-both-worlds approach, as shown in Chapter 14. Chapter 14 also presents an extension to XSLT that removes the verbosity that gets in the way of pure XSLT-based generators.

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.

12.5. Generating Pretty Printers

Problem

You need tools to help debug your application. In particular, you want the ability to render binary messages in human-readable form.

Solution

When developing messaging applications, developers often hand-code pretty printers because they make debugging these applications considerably easier. However, this kind of code can be generated if you have a message repository. This solution shows how to reuse the message switch generator from Recipe 12.2:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xslt [
  <!--Used to control code intenting -->
  <!ENTITY INDENT "    ">
  <!ENTITY INDENT2 "&INDENT;&INDENT;">
  <!ENTITY LS "&lt;&lt;">
]>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<!-- This pretty-printer generator needs a message switch so we -->
<!-- reuse the one we already wrote. -->
<xsl:import href="messageSwitch.xslt"/>
   
  <!--The directory to generate code -->
  <xsl:param name="generationDir" select=" 'src/' "/>
  <!--The C++ header file name -->
  <xsl:param name="prettyPrintHeader" select=" 'prettyPrint.h' "/>
  <!--The C++ source file name -->
  <xsl:param name="prettyPrintSource" select=" 'prettyPrint.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" /> 
   
  <xsl:template match="MessageRepository">
    <xsl:document href="{concat($generationDir,$prettyPrintHeader)}">
      <xsl:text>void prettyPrintMessage</xsl:text>
      <xsl:text>(ostream&amp; stream, const Message&amp; msg);&#xa;</xsl:text>
      <xsl:apply-templates select="DataTypes/Structure" mode="declare"/>
    </xsl:document>
   
    <xsl:document href="{concat($generationDir,$prettyPrintSource)}">
      <xsl:apply-imports/>
      <xsl:apply-templates select="DataTypes/Structure" mode="printers"/>
    </xsl:document>
      
  </xsl:template>     
   
<!--Override the message processing function name from -->
<!-- messageSwitch.xslt to customize the function -->
<!-- signature to take a stream -->
<xsl:template name="process-function">
<xsl:text>void prettyPrintMessage</xsl:text>
<xsl:text>(ostream&amp; stream, const Message&amp; msg)</xsl:text>
</xsl:template>
   
<!--Override case action from messageSwitch.xslt to generate -->
<!-- call to prettyPrinter for message data -->
<xsl:template name="case-action">
 <xsl:text>   prettyPrint(stream, *static_cast&lt;const </xsl:text>
 <xsl:value-of select="DataTypeName"/>
 <xsl:text>*&gt;(msg.getData())) ;
         break;</xsl:text>
</xsl:template>
   
<!--Generate declarations for each message data type -->
<xsl:template match="Structure" mode="declare">
<!--Forward declare the message data class -->
<xsl:text>class </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text> ;&#xa;</xsl:text>
<!--Forward declare the message prettyPrint function -->
<xsl:text>ostream prettyPrint(ostream &amp; stream, const </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text>&amp; data);&#xa;</xsl:text>
</xsl:template>
   
<!--Generate the body of a pretty-printer -->
<xsl:template match="Structure" mode="printers">
<xsl:text>ostream prettyPrint(ostream &amp; stream, const </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text>&amp; data)&#xa;</xsl:text>
<xsl:text>{&#xa;</xsl:text>
<xsl:text>&INDENT;stream &#xa;</xsl:text>  
<xsl:text>&INDENT2;&LS; "</xsl:text>
<xsl:value-of select="Name"/>
<xsl:text>" &LS;  endl  &LS; "{"  &LS; endl &#xa;</xsl:text>  
  <xsl:for-each select="Members/Member">
      <xsl:text>&INDENT2;&LS; "</xsl:text>
      <xsl:value-of select="Name"/>: " &LS; <xsl:text/>
      <xsl:apply-templates 
                 select="key('dataTypes',DataTypeName)" mode="print">
        <xsl:with-param name="name" select="Name"/>
      </xsl:apply-templates> 
      <xsl:text>&#xa;</xsl:text> 
  </xsl:for-each>
  <xsl:text>&INDENT2;&LS; "}"  &LS; endl ; &#xa;</xsl:text>
  <xsl:text>&INDENT;return stream ;&#xa;</xsl:text>
  <xsl:text>}&#xa;&#xa;</xsl:text> 
</xsl:template>
   
<!--Nested structures invoke the pretty-printer for that structure -->
<xsl:template match="Structure" mode="print">
  <xsl:param name="name"/>
  <xsl:text>prettyPrint(stream, data.get_</xsl:text>
  <xsl:value-of select="$name"/><xsl:text>())</xsl:text>
</xsl:template>
   
<!--We assume there is a get function for each -->
<!-- primitive component of the message -->
<xsl:template match="*" mode="print">
  <xsl:param name="name"/>
  <xsl:text>data.get_</xsl:text>
  <xsl:value-of select="$name"/>() &lt;&lt; endl<xsl:text/>
</xsl:template>
   
</xsl:stylesheet>

The following source file is generated. We omit the header since it contains only declarations:

#include <messages/ADD_STOCK_ORDER.h>
#include <messages/ADD_STOCK_ORDER_ACK.h>
#include <messages/ADD_STOCK_ORDER_NACK.h>
#include <messages/CANCEL_STOCK_ORDER.h>
#include <messages/CANCEL_STOCK_ORDER_ACK.h>
#include <messages/CANCEL_STOCK_ORDER_NACK.h>
#include <messages/TEST.h>
   
#include <transport/Message.h>
#include <transport/MESSAGE_IDS.h>
   
   
   
void prettyPrintMessage(ostream& stream, const Message& msg)
{
  switch (msg.getId())
  {
  
    case ADD_STOCK_ORDER_ID:
         prettyPrint(stream, *static_cast<const 
         AddStockOrderData*>(msg.getData())) ;
         break;
    case ADD_STOCK_ORDER_ACK_ID:
         prettyPrint(stream, *static_cast<const 
         AddStockOrderAckData*>(msg.getData())) ;
         break;
    case ADD_STOCK_ORDER_NACK_ID:
         prettyPrint(stream, *static_cast<const 
         AddStockOrderNackData*>(msg.getData())) ;
         break;
    case CANCEL_STOCK_ORDER_ID:
         prettyPrint(stream, *static_cast<const 
         CancelStockOrderData*>(msg.getData())) ;
         break;
    case CANCEL_STOCK_ORDER_ACK_ID:
         prettyPrint(stream, *static_cast<const 
         CancelStockOrderAckData*>(msg.getData())) ;
         break;
    case CANCEL_STOCK_ORDER_NACK_ID:
         prettyPrint(stream, *static_cast<const 
         CancelStockOrderNackData*>(msg.getData())) ;
         break;
    case TEST_ID:
         prettyPrint(stream, *static_cast<const TestData*>(msg.getData())) ;
         break;
    return false ;
  }
}
  ostream prettyPrint(ostream & stream, const TestData& data)
{
    stream 
        << "TestData" <<  endl  << "{"  << endl 
        << "order: " << prettyPrint(stream, data.get_order())
        << "cancel: " << prettyPrint(stream, data.get_cancel())
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const AddStockOrderData& data)
{
    stream 
        << "AddStockOrderData" <<  endl  << "{"  << endl 
        << "symbol: " << data.get_symbol() << endl
        << "quantity: " << data.get_quantity() << endl
        << "side: " << data.get_side() << endl
        << "type: " << data.get_type() << endl
        << "price: " << data.get_price() << endl
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const AddStockOrderAckData& data)
{
    stream 
        << "AddStockOrderAckData" <<  endl  << "{"  << endl 
        << "orderId: " << data.get_orderId() << endl
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const AddStockOrderNackData& data)
{
    stream 
        << "AddStockOrderNackData" <<  endl  << "{"  << endl 
        << "reason: " << data.get_reason() << endl
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const CancelStockOrderData& data)
{
    stream 
        << "CancelStockOrderData" <<  endl  << "{"  << endl 
        << "orderId: " << data.get_orderId() << endl
        << "quantity: " << data.get_quantity() << endl
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const CancelStockOrderAckData& data)
{
    stream 
        << "CancelStockOrderAckData" <<  endl  << "{"  << endl 
        << "orderId: " << data.get_orderId() << endl
        << "quantityRemaining: " << data.get_quantityRemaining() << endl
        << "}"  << endl ; 
    return stream ;
}
   
ostream prettyPrint(ostream & stream, const CancelStockOrderNackData& data)
{
    stream 
        << "CancelStockOrderNackData" <<  endl  << "{"  << endl 
        << "orderId: " << data.get_orderId() << endl
        << "reason: " << data.get_reason() << endl
        << "}"  << endl ; 
    return stream ;
}

Discussion

This code-generation recipe attacks the pretty-printing problem head on by literally generating the pretty-print code for each message. Following this example is simple, and the results are effective. However, you could approach the problem more generally, and in the process create a more useful code generator.

Specifically, you can break the pretty-printing process into two stages. One stage is the process of parsing a monolithic message into its constituent parts. The other is the process of taking those parts and formatting them into human-readable text.

Looking at the problem in this way changes the solution from the generation of a single-purpose set of functions (a pretty printer) to the generation of a more generic message parser. Such parsers are usually event driven. Readers familiar with the Simple API for XML (SAX) will recognize this style of processing. The stylesheet used to generate a message parser is a variation of the pretty-print generator. Instead of sending message components to a stream, it sends parse events to a handler:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xslt [
  <!--Used to control code intenting -->
  <!ENTITY INDENT "    ">
  <!ENTITY INDENT2 "&INDENT;&INDENT;">
  <!ENTITY LS "&lt;&lt;">
]>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<!-- This message parse generator needs a message switch so we -->
<!-- reuse the one we already wrote. -->
<xsl:import href="messageSwitch.xslt"/>
   
  <!--The directory to generate code -->
  <xsl:param name="generationDir" select=" 'src/' "/>
  <!--The C++ header file name -->
  <xsl:param name="msgParseHeader" select=" 'msgParse.h' "/>
  <!--The C++ source file name -->
  <xsl:param name="msgParseSource" select=" 'msgParse.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" /> 
   
  <xsl:template match="MessageRepository">
    <xsl:document href="{concat($generationDir,$msgParseHeader)}">
      <xsl:text>void parseMessage</xsl:text>
       <xsl:text>(MessageHandler&amp; handler, const Message&amp; msg);&#xa;
       </xsl:text>
      <xsl:apply-templates select="DataTypes/Structure" mode="declare"/>
    </xsl:document>
   
    <xsl:document href="{concat($generationDir,$msgParseSource)}">
      <xsl:apply-imports/>
      <xsl:apply-templates select="DataTypes/Structure" mode="parsers"/>
    </xsl:document>
      
  </xsl:template>     
   
<!--Override the message-processing function name from -->
<!-- messageSwitch.xslt to customize the function signature --> 
<!-- to take a handler -->
<xsl:template name="process-function">
<xsl:text>void parseMessage</xsl:text>
<xsl:text>(MessageHandler&amp; handler, const Message&amp; msg)</xsl:text>
</xsl:template>
   
<!--Override case action from messageSwitch.xslt to generate -->
<!-- call to parse for message data -->
<xsl:template name="case-action">
 <xsl:text>   parse(handler, *static_cast&lt;const </xsl:text>
 <xsl:value-of select="DataTypeName"/>
 <xsl:text>*&gt;(msg.getData())) ;
         break;</xsl:text>
</xsl:template>
   
<!--Generate declarations for each message data type -->
<xsl:template match="Structure" mode="declare">
<!--Forward declare the message data class -->
<xsl:text>class </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text> ;&#xa;</xsl:text>
<!--Forward declare the message parse function -->
<xsl:text>void parse(MessageHandler &amp; handler, const </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text>&amp; data);&#xa;</xsl:text>
</xsl:template>
   
<!--Generate the body of a parser -->
<xsl:template match="Structure" mode="parsers">
<xsl:text>void parse(MessageHandler &amp; handler, const </xsl:text>
<xsl:value-of select="Name"/>
<xsl:text>&amp; data)&#xa;</xsl:text>
<xsl:text>{&#xa;</xsl:text>
<xsl:text>&INDENT;handler.beginStruct("</xsl:text>  
<xsl:value-of select="Name"/>
<xsl:text>") ;&#xa;</xsl:text>  
  <xsl:for-each select="Members/Member">
      <xsl:apply-templates 
           select="key('dataTypes',DataTypeName)" mode="parse">
        <xsl:with-param name="name" select="Name"/>
      </xsl:apply-templates> 
  </xsl:for-each>
<xsl:text>&INDENT;handler.endStruct("</xsl:text>  
<xsl:value-of select="Name"/>
<xsl:text>") ;&#xa;</xsl:text>  
  <xsl:text>}&#xa;&#xa;</xsl:text> 
</xsl:template>
   
<!--Nested structures invoke the parser for that structure -->
<xsl:template match="Structure" mode="parse">
  <xsl:param name="name"/>
  <xsl:text>&INDENT;parse(handler, data.get_</xsl:text>
  <xsl:value-of select="$name"/><xsl:text>());&#xa;</xsl:text>
</xsl:template>
   
<!--We assume there is a get function for each -->
<!-- primitive component of the message -->
<xsl:template match="*" mode="parse">
  <xsl:param name="name"/>
  <xsl:text>&INDENT;handler.field("</xsl:text>
  <xsl:value-of select="$name"/>","<xsl:text/>
  <xsl:value-of select="Name"/>",<xsl:text/>
  <xsl:text>data.get_</xsl:text>
  <xsl:value-of select="$name"/>()<xsl:text/>
  <xsl:text>);&#xa;</xsl:text> 
</xsl:template>
   
</xsl:stylesheet>

It produces parse functions that look like the following code:

void parse(MessageHandler & handler, const AddStockOrderData& data)
{
    handler.beginStruct("AddStockOrderData") ;
    handler.field("symbol","StkSymbol",data.get_symbol());
    handler.field("quantity","Shares",data.get_quantity());
    handler.field("side","BuyOrSell",data.get_side());
    handler.field("type","OrderType",data.get_type());
    handler.field("price","Real",data.get_price());
    handler.endStruct("AddStockOrderData")





 ;
}

12.6. Generating a Test Data-Entry Web Client

Problem

You want to test a process in isolation by entering information in a form and having the information converted into a message that is sent to a process.

Solution

This example generates a test data-entry tool’s client side. It takes the shape of an HTML-based form that can be used to enter the fields that make up a message. It specifies that the form data be handled by a CGI that is generated in Recipe 12.7:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" />
   
  <xsl:param name="message"/>
   
  <!--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" /> 
  
  <xsl:template match="/">
    <html>
      <head>
        <title><xsl:value-of select="$message"/> Entry</title>
      </head>
      <body bgcolor="#FFFFFF" text="#000000">
        <h1><xsl:value-of select="$message"/> Entry</h1>
        <form name="{concat($message,'Form')}" method="post" 
        action="{concat('/cgi-bin/',$message,'Process.pl')}">
          <xsl:apply-templates select="*/Messages/Message[Name=$message]"/>
          <br/><center><input type="submit" name="Submit" value="Submit"/></center>
        </form>
      </body>
    </html>
  </xsl:template>     
   
  <xsl:template match="Message">
    <xsl:apply-templates select="key('dataTypes',DataTypeName)">
      <xsl:with-param name="field" select="Name"/>
    </xsl:apply-templates>
  </xsl:template>  
   
<xsl:template match="Structure">
  <xsl:param name="field"/>
  <table width="100%" border="0" cellspacing="1" cellpadding="1">
    <tbody>
      <xsl:for-each select="Members/Member">
        <tr>
          <td valign="top"><xsl:value-of select="Name"/></td>
          <td>
            <xsl:apply-templates select="key('dataTypes',DataTypeName)">
              <xsl:with-param name="field" 
                        select="concat($field,'_',Name)"/>
            </xsl:apply-templates>
          </td>
        </tr>
     </xsl:for-each>
    </tbody>
  </table>
</xsl:template>
   
<xsl:template match="*">
  <xsl:param name="field"/>
  <input type="text" name="{$field}" size="30"/>
</xsl:template>
  
</xsl:stylesheet>

Discussion

Generating the UI in HTML is one of the easiest ways to auto-generate a test-data front-end; however, it is not the only way. You might also want to generate a text-based front-end that prompts and reads input from stdin. If you feel ambitious, you might generate a GUI front end. One advantage of not using HTML is that you can combine the functionality of this example and Recipe 12.7 into a single application. However, do not be surprised if such a generator is more complex than the generators produced in these examples; the HTML-CGI approach builds on a substantial existing infrastructure present in the browser and web server.

An important extension to this recipe would generate validation code in JavaScript or VBScript. Again, the quality of this validation code depends on what type of metadata is kept in the repository. You might want to extend the repository to store minimum and maximum values and/or regular expressions for use in data validation.

12.7. Generating Test-Entry Web CGI

Problem

You want to test a process in isolation by processing text data received from a form and converting that data into a physical message that can be sent to the process under test.

Solution

This server generator goes along with the client generator created in Recipe 12.6. This section shows only a piece of the solution, but the discussion gives further detail.

The generated server is a C++ CGI program. You need it to be C++ (or C) because the form input will be converted into a binary message that is laid out according to the C memory model. It assumes the C++ processes that ultimately consume these messages expect them as binary data. The generated CGI uses Enterprise Integration Technologies’ libcgi (http://www.landfield.com/hypermail/source/libcgi/) to simplify the common CGI tasks such as query-string parsing:

<!DOCTYPE xslt [
  <!--Used to control code intenting -->
  <!ENTITY INDENT "    ">
]>
<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="source">
        <xsl:with-param name="msg" select="Name"/>
      </xsl:apply-templates>
    </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 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:param name="msg"/>
   
  <xsl:document href="{concat($generationDir,Name,'CGI',$sourceExt)}">
   
<xsl:text>
#include &lt;stdio.h&gt;
#include "cgi.h"
#include "</xsl:text>
<xsl:value-of select="Name"/><xsl:value-of select="$headerExt"/>
<xsl:text>"
   
void cgi_main(cgi_info *cgi)
{
    </xsl:text>
    <xsl:value-of select="Name"/> 
    <xsl:text> data ;
    form_entry* form_data = get_form_entries(cgi) ;   
</xsl:text>
  
  <xsl:for-each select="Members/Member">
    <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="variables">
      <xsl:with-param name="field" select="concat($msg,'_',Name)"/>
      <xsl:with-param name="var" select="Name"/>
    </xsl:apply-templates>
   </xsl:for-each>
   
  <xsl:for-each select="Members/Member">
    <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="load">
      <xsl:with-param name="field" select="concat('data.',Name)"/>
      <xsl:with-param name="var" select="Name"/>
    </xsl:apply-templates>
  </xsl:for-each>
   
<xsl:text>
&INDENT;//Enque data to the process being tested 
&INDENT;enqueData(data) ;
   
}</xsl:text>   
</xsl:document>
</xsl:template>
   
<!-- Declare and initialize variables for each field -->
<xsl:template match="Structure" mode="variables">
  <xsl:param name="field"/>
  <xsl:param name="var"/>
      <xsl:for-each select="Members/Member">
            <xsl:apply-templates select="key('dataTypes',DataTypeName)"
                mode="variables">
              <xsl:with-param name="field" select="concat($field,'_',Name)"/>
              <xsl:with-param name="var" select="$var"/>
            </xsl:apply-templates>
     </xsl:for-each>
</xsl:template>
   
<xsl:template match="*" mode="variables">
  <xsl:param name="field"/>
  <xsl:param name="var"/>
  
  <xsl:text>&INDENT;const char * </xsl:text>
  <xsl:value-of select="$var"/>
  <xsl:text> = parmval(form_data, "</xsl:text>
  <xsl:value-of select="$field"/>
  <xsl:text>");&#xa;</xsl:text>
</xsl:template>
   
<!-- Initialize data form the converted value -->
<xsl:template match="Structure" mode="load">
  <xsl:param name="field"/>
  <xsl:param name="var"/>
  <xsl:for-each select="Members/Member">
        <xsl:apply-templates select="key('dataTypes',DataTypeName)" mode="load">
          <xsl:with-param name="field" select="concat($field,'.',Name)"/>
          <xsl:with-param name="var" select="concat($field,'_',Name)"/>
        </xsl:apply-templates>
  </xsl:for-each>
</xsl:template>
   
<xsl:template match="Primitive" mode="load">
  <xsl:param name="field"/>
  <xsl:param name="var"/>
 
  <xsl:text>&INDENT;</xsl:text>
  <xsl:value-of select="$field"/>  
  <xsl:text> = </xsl:text>
  <xsl:value-of select="Name"/>
  <xsl:text>(</xsl:text>
  <xsl:value-of select="$var"/>
  <xsl:text>);&#xa;</xsl:text>
</xsl:template>
  
<xsl:template match="Enumeration" mode="load">
 <xsl:param name="field"/>
  <xsl:param name="var"/>
 
  <xsl:text>&INDENT;</xsl:text>
  <xsl:value-of select="$field"/>  
  <xsl:text> = Enum</xsl:text>
  <xsl:value-of select="Name"/>
  <xsl:text>NameToVal(</xsl:text>
  <xsl:value-of select="$var"/>
  <xsl:text>");&#xa;</xsl:text>
</xsl:template>
   
</xsl:stylesheet>

If run against our repository, this XSLT transformation generates a cgi program for each message. For example:

#include <stdio.h>
#include "cgi.h"
#include "msg_ids.h"
#include "AddStockOrderData.h"
   
void cgi_main(cgi_info *cgi)
{
    AddStockOrderData data ;
    form_entry* form_data = get_form_entries(cgi) ;   
    const char * symbol = parmval(form_data, "ADD_STOCK_ORDER_symbol");
    const char * quantity = parmval(form_data, "ADD_STOCK_ORDER_quantity");
    const char * side = parmval(form_data, "ADD_STOCK_ORDER_side");
    const char * type = parmval(form_data, "ADD_STOCK_ORDER_type");
    const char * price = parmval(form_data, "ADD_STOCK_ORDER_price");
    data.symbol = StkSymbol(symbol);
    data.quantity = Shares(quantity);
    data.side = EnumBuyOrSellNameToVal(side);
    data.type = EnumOrderTypeNameToVal(type);
    data.price = Real(price);
   
    //Enque data to the process being tested 
    enqueData(ADD_STOCK_ORDER,data) ;
   
}

Discussion

The solution makes several assumptions. First, it assumes that the primitive types (e.g., Shares) are classes with constructors that convert from strings to the type’s internal representation (e.g., int). Second, it assumes the existence of conversion functions for converting symbolic names of enumeration constants to their integral values. As you might guess, these functions can be generated easily from information in the repository. We leave that as an exercise. Third, the generated code assumes a function, called enqueData, that knows how to send a message to the correct process under test. The details of how this function interacts with the specific middleware and how it locates the particular queue vary from implementation to implementation.

The bottom line is that you have to tweak this code generator to suit your needs. However, once a sufficiently rich metadata repository is established, a significant amount of automation is possible. The functionality automated in this example and Recipe 12.6 could easily keep a programmer busy full time. This is especially true if the messages the application handles are in constant flux.

12.8. Generating Code from UML Models via XMI

Problem

You want to generate code from Unified Modeling Language (UML) specifications but are unhappy with the results produced by your UML tools’ native-code generator.

Solution

XMI is a standard XML-based representation of UML that many UML modeling tools (such as Rational Rose) support. Although the stated purpose of XMI is to allow interchange of modeling information between different tools, it also can be the basis for code generation.

Most UML tools support code generation but rarely generate more than a skeleton of the object model. For instance, all UML-based generators I am aware of only consider information in class diagrams.

Here you will generate code for the State Design Pattern [3] that combines information from both a class diagram (Figure 12-1) and a state chart (Figure 12-2).

Class diagram representing states
Figure 12-2. Class diagram representing states
State chart for answering machine
Figure 12-3. State chart for answering machine

The code generator assumes that the designer followed these conventions:

  1. The state context class has the stereotype StateMachine.

  2. The abstract base class of the actual state classes has the stereotype State.

  3. All operations in the state context that forward their implementation to the state classes have the stereotype delegate (indicating that they delegate their responsibility to the state).

  4. All state context operations used by the states to implement state-machine behavior have the stereotype action.

  5. The only exception to (4) is the single operation that should be invoked when the state does not know what to do. This operation has stereotype default and is generally used for error handling.

  6. The UML state chart states use the same names as the concrete classes derived from the State interface (2).

  7. The actions and guards associated with transitions refer to the context and use the exact operation names. An action is the code executed upon a transition and a guard is a condition that must be true for the transition to be chosen.

The next example shows a state machine that implements the basic functionality of an answering machine when you retrieve your messages.

The XMI generated from this simple design cannot be shown in its entirety because it is huge. XMI uses horrendously long and arduous naming conventions. This chapter shows a fragment containing the AnsweringMachineState class to give the flavor of it. However, even this has been elided:

<Foundation.Core.Class xmi.id="S.10011">
  <Foundation.Core.ModelElement.name>AnsweringMachineState
  </Foundation.Core.ModelElement.name>
  <Foundation.Core.ModelElement.stereotype>
    <Foundation.Extension_Mechanisms.Stereotype xmi.idref="G.22"/>
    <!-- state -->
  </Foundation.Core.ModelElement.stereotype>
  <Foundation.Core.GeneralizableElement.specialization>
    <Foundation.Core.Generalization xmi.idref="G.91"/>
    <!-- {Connected-&gt;AnsweringMachineState}{3D6782A402EE} -->
    <Foundation.Core.Generalization xmi.idref="G.92"/>
    <!-- {HandlingMsg-&gt;AnsweringMachineState}{3D6782EF0119} -->
    <Foundation.Core.Generalization xmi.idref="G.93"/>
    <!-- {Start-&gt;AnsweringMachineState}{3D67B2FD004E} -->
    <Foundation.Core.Generalization xmi.idref="G.94"/>
    <!-- {GoodBye-&gt;AnsweringMachineState}{3D67B31B02AF} -->
  </Foundation.Core.GeneralizableElement.specialization>
  <Foundation.Core.Classifier.associationEnd>
    <Foundation.Core.AssociationEnd xmi.idref="G.25"/>
  </Foundation.Core.Classifier.associationEnd>
  <Foundation.Core.Classifier.feature>
    <Foundation.Core.Operation xmi.id="S.10012">
      <Foundation.Core.ModelElement.name>doCmd</Foundation.Core.ModelElement.name>
      <Foundation.Core.ModelElement.visibility xmi.value="public"/>
      <Foundation.Core.Feature.ownerScope xmi.value="instance"/>
      <Foundation.Core.BehavioralFeature.isQuery xmi.value="false"/>
      <Foundation.Core.Operation.specification/>
      <Foundation.Core.Operation.isPolymorphic xmi.value="false"/>
      <Foundation.Core.Operation.concurrency xmi.value="sequential"/>
      <Foundation.Core.ModelElement.stereotype>
        <Foundation.Extension_Mechanisms.Stereotype xmi.idref="G.23"/>
        <!-- pure -->
      </Foundation.Core.ModelElement.stereotype>
      <Foundation.Core.BehavioralFeature.parameter>
        <Foundation.Core.Parameter xmi.id="G.76">
          
<Foundation.Core.ModelElement.name>cntx</Foundation.Core.ModelElement.name>
          <Foundation.Core.ModelElement.visibility xmi.value="private"/>
          <Foundation.Core.Parameter.defaultValue>
            <Foundation.Data_Types.Expression>
              <Foundation.Data_Types.Expression.language/>
              <Foundation.Data_Types.Expression.body/>
            </Foundation.Data_Types.Expression>
          </Foundation.Core.Parameter.defaultValue>
          <Foundation.Core.Parameter.kind xmi.value="inout"/>
          <Foundation.Core.Parameter.type>
            <Foundation.Core.Class xmi.idref="S.10001"/>
            <!-- AnsweringMachine -->
          </Foundation.Core.Parameter.type>
        </Foundation.Core.Parameter>
        <Foundation.Core.Parameter xmi.id="G.77">
          
<Foundation.Core.ModelElement.name>cmd</Foundation.Core.ModelElement.name>
          <Foundation.Core.ModelElement.visibility xmi.value="private"/>
          <Foundation.Core.Parameter.defaultValue>
            <Foundation.Data_Types.Expression>
              <Foundation.Data_Types.Expression.language/>
              <Foundation.Data_Types.Expression.body/>
            </Foundation.Data_Types.Expression>
          </Foundation.Core.Parameter.defaultValue>
          <Foundation.Core.Parameter.kind xmi.value="inout"/>
          <Foundation.Core.Parameter.type>
            <Foundation.Core.DataType xmi.idref="G.65"/>
            <!-- Command -->
          </Foundation.Core.Parameter.type>
        </Foundation.Core.Parameter>
        <Foundation.Core.Parameter xmi.id="G.78">
          <Foundation.Core.ModelElement.name>doCmd.Return
          </Foundation.Core.ModelElement.name>
          <Foundation.Core.ModelElement.visibility xmi.value="private"/>
          <Foundation.Core.Parameter.defaultValue>
            <Foundation.Data_Types.Expression>
              <Foundation.Data_Types.Expression.language/>
              <Foundation.Data_Types.Expression.body/>
            </Foundation.Data_Types.Expression>
          </Foundation.Core.Parameter.defaultValue>
          <Foundation.Core.Parameter.kind xmi.value="return"/>
          <Foundation.Core.Parameter.type>
            <Foundation.Core.DataType xmi.idref="G.67"/>
            <!-- void -->
          </Foundation.Core.Parameter.type>
        </Foundation.Core.Parameter>
      </Foundation.Core.BehavioralFeature.parameter>
    </Foundation.Core.Operation>
  </Foundation.Core.Classifier.feature>
</Foundation.Core.Class>

The following large XSLT example converts the XMI into a C++ implementation of this state machine according to the State Design Pattern. Out of sheer desperation, I use XML entities to abbreviate long XMI names. I don’t usually recommend this practice, but in this case, it was the only way to make the XSLT fit reasonably on the page. It also reduces the inherent noise level of the XMI DTD. You will also notice that keys are used extensively because XMI makes heavy use of cross references in the form of xmi.id and xmi.idref attributes:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xslt [
  <!--=======================================================-->
  <!-- XMI's high-level organization constructs              -->
  <!--=======================================================-->
  <!ENTITY BE "Behavioral_Elements">
  <!ENTITY SM "&BE;.State_Machines">
  <!ENTITY SMC "&SM;.StateMachine.context">
  <!ENTITY SMT "&SM;.StateMachine.top">
  <!ENTITY MM "Model_Management.Model">
  <!ENTITY FC "Foundation.Core">
  <!ENTITY FX "Foundation.Extension_Mechanisms">

  <!--=======================================================-->
  <!-- Abbreviations for the basic elements of a XMI         -->
  <!-- file that are of most interest to this stylesheet.  -->
  <!--=======================================================-->
  <!--The model as a whole -->
  <!ENTITY MODEL "XMI/XMI.content/&MM;">
  <!--Some generic kind of UML element -->
  <!ENTITY ELEM "&FC;.Namespace.ownedElement">
  <!--Elements of state machines --> 
  <!ENTITY STATEMACH "&MODEL;/&ELEM;/&SM;.StateMachine">
  <!ENTITY STATE "&SM;.CompositeState">
  <!ENTITY SUBSTATE "&STATE;.substate">
  <!ENTITY PSEUDOSTATE "&SM;.PseudoState">
  <!ENTITY PSEUDOSTATE2 "&SMT;/&STATE;/&SUBSTATE;/&PSEUDOSTATE;">
  <!ENTITY ACTION "&BE;.Common_Behavior.ActionSequence/&BE;.Common_Behavior.ActionSequence.action">
  <!ENTITY GUARD "&SM;.Transition.guard/&SM;.Guard/&SM;.Guard.expression">
  <!--The association as a whole -->
  <!ENTITY ASSOC "&FC;.Association">
  <!--The connection part of the association-->
  <!ENTITY CONN "&ASSOC;.connection">
  <!--The ends of an association. -->
  <!ENTITY END "&ASSOC;End">
  <!ENTITY CONNEND "&CONN;/&END;">
  <!ENTITY ENDType "&END;.type">
  <!-- A UML class -->
  <!ENTITY CLASS "&FC;.Class">
  <!--The name of some UML entity -->
  <!ENTITY NAME "&FC;.ModelElement.name">
  <!--A operation -->
  <!ENTITY OP "&FC;.Operation">
  <!-- A parameter -->
  <!ENTITY PARAM "&FC;.Parameter">
  <!ENTITY PARAM2 "&FC;.BehavioralFeature.parameter/&PARAM;">
  <!-- The data type of a parameter -->
  <!ENTITY PARAMTYPE "&PARAM;.type/&FC;.DataType">
  <!--A UML sterotype -->
  <!ENTITY STEREOTYPE "&FC;.ModelElement.stereotype/&FX;.Stereotype">
  <!-- A Supertype relation (inheritance) -->
  <!ENTITY SUPERTYPE "&FC;.Generalization.supertype">
  <!ENTITY GENERALIZATION "&FC;.GeneralizableElement.generalization/&FC;.Generalization">
  <!--=======================================================-->
  <!--Formatting                                             -->
  <!--=======================================================-->
  
  <!--Used to control code indenting -->
  <!ENTITY INDENT "    ">
  <!ENTITY INDENT2 "&INDENT;&INDENT;">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  
  <!--Index classes by their id -->
  <xsl:key name="classKey" match="&CLASS;" use="@xmi.id"/>
  <xsl:key name="classKey" match="&CLASS;" use="&NAME;"/>
  
  <!-- Index Stereoptypes by both name and xmi.id -->
  <xsl:key name="stereotypeKey" match="&FX;.Stereotype" use="@xmi.id"/>
  <xsl:key name="stereotypeKey" match="&FX;.Stereotype" use="&NAME;"/>
  
  <!--Index data types by their id -->
  <xsl:key name="dataTypeKey" match="&FC;.DataType" use="@xmi.id"/>
  
  <!--Index associations by their end classes -->
  <xsl:key name="associationKey" match="&ASSOC;" use="&CONNEND;/&ENDType;/&FC;.Interface/@xmi.idref"/>
  <xsl:key name="associationKey" match="&ASSOC;" use="&CONNEND;/&ENDType;/&CLASS;/@xmi.idref"/>
  
  <!--Index generalizations by their id -->
  <xsl:key name="generalizationKey" match="&FC;.Generalization" use="@xmi.id"/>

  <!--Index states and pseudo states together by their id -->
  <xsl:key name="stateKey" match="&SM;.SimpleState" use="@xmi.id"/>
  <xsl:key name="stateKey" match="&PSEUDOSTATE;" use="@xmi.id"/>
  
  <!--Index state transitions by their id -->
  <xsl:key name="transKey" match="&SM;.Transition" use="@xmi.id"/>
  
  <!--Index transitions events by their id -->
  <xsl:key name="eventsKey" match="&SM;.SignalEvent" use="@xmi.id"/>
  
  <!-- The xmi ids of stereotypes used to encode State Pattern in UML -->
  <xsl:variable name="STATE_MACH" 
                select="key('stereotypeKey','StateMachine')/@xmi.id"/>
  <xsl:variable name="STATE"
                select="key('stereotypeKey','state')/@xmi.id"/>
  <xsl:variable name="DELEGATE" 
                select="key('stereotypeKey','delegate')/@xmi.id"/>
  <xsl:variable name="ACTION" 
                select="key('stereotypeKey','action')/@xmi.id"/>
  <xsl:variable name="DEFAULT"
                select="key('stereotypeKey','default')/@xmi.id"/>

  <xsl:variable name="PURE" 
                select="key('stereotypeKey','pure')/@xmi.id"/>
  
  <!-- We use modes to generate the source in 5 steps -->
  <xsl:template match="/">

    <!-- Declare state interface -->  
    <xsl:apply-templates mode="stateInterfaceDecl"
          select="&MODEL;/&ELEM;/&CLASS;[&STEREOTYPE;/@xmi.idref =
                                                        $STATE_MACH]"/>

    <!-- Declare concrete states -->  
    <xsl:apply-templates mode="concreteStatesDecl"
         select="&MODEL;/&ELEM;/&CLASS;[not(&STEREOTYPE;)]"/>

    <!-- Declare state context class -->  
    <xsl:apply-templates mode="stateDecl" 
          select="&MODEL;/&ELEM;/&CLASS;[&STEREOTYPE;/@xmi.idref = 
                                                        $STATE_MACH]"/>
         
    <!--Implement states -->  
    <xsl:apply-templates mode="concreteStatesImpl" 
        select="&MODEL;/&ELEM;"/>

    <!--Implement context -->  
    <xsl:apply-templates mode="stateContextImpl"
         select="&MODEL;/&ELEM;/&CLASS;[&STEREOTYPE;/@xmi.idref =
                                    $STATE_MACH]"/>
        
  </xsl:template>
  
  <!-- STATE CONTEXT DECLARATION -->
  <xsl:template match="&CLASS;" mode="stateDecl">
  
    <!-- Find the class associated with this state context that -->
    <!-- is a state implementation. This is the type of the state -->
    <!-- machines corrent state member variable -->
    <xsl:variable name="stateImplClass">
      <xsl:variable name="stateClassId" select="@xmi.id"/>
      <xsl:for-each select="key('associationKey',$stateClassId)">
        <xsl:variable name="assocClassId" 
                      select="&CONNEND;[&ENDType;/*/@xmi.idref !=
                                  $stateClassId]/&ENDType;/*/@xmi.idref"/>
        <xsl:if test="key('classKey',$assocClassId)/&STEREOTYPE;/@xmi.idref 
                                                                 = $STATE">
          <xsl:value-of select="key('classKey',$assocClassId)/&NAME;"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="className" select="&NAME;"/>
    
    <xsl:text>&#xa;class </xsl:text>
    <xsl:value-of select="$className"/>
    <xsl:text>&#xa;{&#xa;public:&#xa;&#xa;</xsl:text>

    
    <!--Ctor decl -->
    <xsl:text>&INDENT;</xsl:text>
    <xsl:value-of select="$className"/>::<xsl:value-of select="$className"/>
    <xsl:text>();&#xa;&#xa;</xsl:text>
   
    <!-- Delegates are operations that defer to the current state -->
    <xsl:apply-templates 
                       select="*/&OP;[&STEREOTYPE;/@xmi.idref = $DELEGATE]" 
                       mode="declare"/>

    <!-- void changeState(AbstractState& newState) -->
    <xsl:text>&INDENT;void changeState(</xsl:text>
    <xsl:value-of select="$stateImplClass"/>
    <xsl:text>&amp; newSate) ;&#xa;</xsl:text>
    
    <xsl:text>&#xa;&#xa;</xsl:text>
    <!-- Non-Delegates are other operations that states -->
    <!-- invoke on the context -->
    <xsl:apply-templates 
              select="*/&OP;[&STEREOTYPE;/@xmi.idref != $DELEGATE]"
              mode="declare"/>
    <xsl:text>&#xa;private:&#xa;&#xa;</xsl:text>
    <xsl:text>&INDENT;</xsl:text>
    <xsl:value-of select="$stateImplClass"/>
    <xsl:text>* m_State ;</xsl:text>
    <xsl:text>&#xa;&#xa;} ;&#xa;&#xa;</xsl:text>
    
  </xsl:template>
  
  <!-- CONCRETE STATES DECLARATION -->
  
  <xsl:template match="&CLASS;" mode="concreteStatesDecl">
    <xsl:text>class </xsl:text>
  <xsl:value-of select="&NAME;"/>
    <xsl:call-template name="baseClass"/>
    <xsl:text>&#xa;{&#xa;public:&#xa;&#xa;</xsl:text>
    <!-- Concrete States are Singletons so we generate an -->
    <!-- instance method -->
    <xsl:text>&INDENT;static </xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>&amp; instance() ;</xsl:text>
    <xsl:text>&#xa;&#xa;private:&#xa;&#xa;</xsl:text>
    <!-- We protect constructors of Singletons-->
    <xsl:text>&INDENT;</xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>() {  } &#xa;</xsl:text>
    <xsl:text>&INDENT;</xsl:text>
    <xsl:value-of select="&NAME;"/>(const <xsl:value-of select="&NAME;"/>
    <xsl:text>&amp;) {  } &#xa;</xsl:text>
    <xsl:text>&INDENT;void operator =(const </xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>&amp;) {  } &#xa;</xsl:text>
    <xsl:apply-templates select="*/&OP;" mode="declare"/>
    <xsl:text>
} ;

</xsl:text>
  </xsl:template>
  
  <!-- Templates used to declare classes with all public members -->
  
  <xsl:template match="&CLASS;" mode="declare">
    <xsl:text>class&#x20;</xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>&#xa;{&#xa;public:&#xa;&#xa;</xsl:text>
    <xsl:apply-templates select="*/&OP;" mode="declare"/>
    <xsl:text>&#xa;} ;&#xa;</xsl:text>
  </xsl:template>
  
  <xsl:template match="&OP;" mode="declare">
    <xsl:variable name="returnTypeId"
                            select="&PARAM2;[&PARAM;.kind/@xmi.value = 
                                                                
'return']/&PARAMTYPE;/@xmi.idref"/>
      <xsl:text>&INDENT;</xsl:text>
    <xsl:if test="&STEREOTYPE;/@xmi.idref = $PURE">
      <xsl:text>virtual </xsl:text>
    </xsl:if>
    <xsl:value-of select="key('dataTypeKey',$returnTypeId)/&NAME;"/>
      <xsl:text>&#x20;</xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>(</xsl:text>
    <xsl:call-template name="parameters"/>
    <xsl:text>)</xsl:text>
    <xsl:if test="&STEREOTYPE;/@xmi.idref = $PURE">
      <xsl:text> = 0 </xsl:text>
    </xsl:if>
    <xsl:text>;&#xa;</xsl:text>
  </xsl:template>

  <!--Eat extra text nodes  --> 
  <xsl:template match="text()" mode="declare"/>
  
  <!-- STATE INTERFACE DECLARATION -->
  <xsl:template match="&CLASS;" mode="stateInterfaceDecl">

    <xsl:text>//Forward declarations&#xa;</xsl:text>
    <xsl:text>class </xsl:text>
    <xsl:value-of select="&NAME;"/>
    <xsl:text>;&#xa;&#xa;</xsl:text>
    
    <xsl:variable name="stateClassId" select="@xmi.id"/>
    <!-- Find the class associated with the state context that -->
    <!-- is a state. -->
    <xsl:for-each select="key('associationKey',$stateClassId)">
      <xsl:variable name="assocClassId"
                    select="&CONNEND;[&ENDType;/*/@xmi.idref != 
                                   $stateClassId]/&ENDType;/*/@xmi.idref"/>
      <xsl:if test="key('classKey',$assocClassId)/&STEREOTYPE;/@xmi.idref = 
                                                                   $STATE">
        <xsl:apply-templates select="key('classKey',$assocClassId)" 
                             mode="declare"/>
      </xsl:if>
    </xsl:for-each>
    <xsl:text>&#xa;&#xa;</xsl:text>
  </xsl:template>
  
  <!--Eat extra text nodes  --> 
  <xsl:template match="text()" mode="stateInterfaceDecl"/>
  
  <!-- STATE CONTEXT IMPLEMENTATION -->
  <xsl:template match="&CLASS;" mode="stateContextImpl">
  
    <xsl:variable name="stateImplClass">
      <xsl:variable name="stateClassId" select="@xmi.id"/>
      <xsl:for-each select="key('associationKey',$stateClassId)">
        <xsl:variable name="assocClassId"
             select="&CONNEND;[&ENDType;/*/@xmi.idref !=
                                 $stateClassId]/&ENDType;/*/@xmi.idref"/>
        <xsl:if test="key('classKey',$assocClassId)/&STEREOTYPE;/@xmi.idref 
                                                                 = $STATE">
          <xsl:value-of select="key('classKey',$assocClassId)/&NAME;"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="className" select="&NAME;"/>
    <xsl:text>//Constructor&#xa;</xsl:text>
    <xsl:value-of select="$className"/>::<xsl:value-of
                                          select="$className"/>
    <xsl:text>()&#xa;</xsl:text>
    <xsl:text>{&#xa;</xsl:text>
    <xsl:text>&INDENT;//Initialize state machine in start state &#xa;</xsl:text>
    <xsl:variable name="startStateName">
      <xsl:call-template name="getStartState">
        <xsl:with-param name="classId" select="@xmi.id"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:text>&INDENT;m_State = &amp;</xsl:text>
    <xsl:value-of select="$startStateName"/>
    <xsl:text>::instance() ;&#xa;</xsl:text>
    <xsl:text>}&#xa;&#xa;</xsl:text>

    <!-- void changeState(AbstractState& newState) -->
    <xsl:text>void </xsl:text>
    <xsl:value-of select="$className"/>
    <xsl:text>::changeState(</xsl:text>
    <xsl:value-of select="$stateImplClass"/>
    <xsl:text>&amp; newState)&#xa;</xsl:text>
    <xsl:text>{&#xa;</xsl:text>
    <xsl:text>&INDENT;m_State = &amp;newState;</xsl:text>
    <xsl:text>&#xa;}&#xa;&#xa;</xsl:text>
    
    <xsl:for-each select="*/&OP;[&STEREOTYPE;/@xmi.idref = $DELEGATE]">
      <xsl:variable name="returnTypeId" 
                             select="&PARAM2;[&PARAM;.kind/@xmi.value =
                                        'return']/&PARAMTYPE;/@xmi.idref"/>
      <xsl:value-of select="key('dataTypeKey',$returnTypeId)/&NAME;"/>
      <xsl:text>&#x20;</xsl:text>
      <xsl:value-of select="$className"/>::<xsl:value-of select="&NAME;"/>
      <xsl:text>(</xsl:text>
      <xsl:call-template name="parameters"/>
      <xsl:text>)&#xa;</xsl:text>
      <xsl:text>{&#xa;</xsl:text>
      <xsl:text>&INDENT;m_State-></xsl:text>
      <xsl:value-of select="&NAME;"/>
      <xsl:text>(*this, </xsl:text>
      <xsl:for-each select="&PARAM2;[&PARAM;.kind/@xmi.value != 'return']">
        <xsl:value-of select="&NAME;"/>
        <xsl:if test="position() != last()">
          <xsl:text>, </xsl:text>
        </xsl:if>
      </xsl:for-each>
      <xsl:text>);&#xa;</xsl:text>
      <xsl:text>}&#xa;&#xa;</xsl:text>
    </xsl:for-each>
    
    <xsl:for-each select="*/&OP;[&STEREOTYPE;/@xmi.idref != $DELEGATE]">
      <xsl:variable name="returnTypeId"
                    select="&PARAM2;[&PARAM;.kind/@xmi.value = 
                                    'return']/&PARAMTYPE;/@xmi.idref"/>
      <xsl:value-of select="key('dataTypeKey',$returnTypeId)/&NAME;"/>
      <xsl:text>&#x20;</xsl:text>
      <xsl:value-of select="$className"/>::<xsl:value-of select="&NAME;"/>
      <xsl:text>(</xsl:text>
      <xsl:call-template name="parameters"/>
      <xsl:text>)&#xa;</xsl:text>
      <xsl:text>{&#xa;</xsl:text>
      <xsl:text>&INDENT;//!TODO: Implement behavior of this action&#xa;</xsl:text>
      <xsl:text>}&#xa;&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="text()" mode="stateContextImpl"/>

  <!-- CONCRETE STATES IMPLEMENTATION -->
  <xsl:template match="&CLASS;" mode="concreteStatesImpl">
    <xsl:variable name="classId" select="@xmi.id"/>
    <xsl:variable name="stateMachine"
                  select="/&STATEMACH;[&SMC;/&CLASS;/@xmi.idref = 
                                                               $classId]"/>
    <!-- For each state in the machine, generate its implementation -->
    <xsl:for-each select="$stateMachine//&PSEUDOSTATE; |
                                        $stateMachine//&SM;.SimpleState">
      <xsl:call-template name="generateState">
        <xsl:with-param name="stateClass" select="key('classKey',&NAME;)"/>
      </xsl:call-template>
    </xsl:for-each>
  </xsl:template>
  
  <!--Eat extra text nodes  --> 
  <xsl:template match="text()" mode="concreteStatesImpl"/>
  
  <xsl:template name="generateState">
    <!--This is a xmi class corresponding the the state -->
    <xsl:param name="stateClass"/>
    <!-- The current context is some state -->
    <xsl:variable name="state" select="."/>
    <xsl:variable name="className" select="&NAME;"/>
    <xsl:if test="$className != $stateClass/&NAME;">
      <xsl:message terminate="yes">State and class do not match!</xsl:message>
    </xsl:if>
    <xsl:for-each select="$stateClass/*/&OP;">
      <xsl:variable name="returnTypeId" 
                    select="&PARAM2;[&PARAM;.kind/@xmi.value =
                                     'return']/&PARAMTYPE;/@xmi.idref"/>
      <xsl:value-of select="key('dataTypeKey',$returnTypeId)/&NAME;"/>
      <xsl:text>&#x20;</xsl:text>
      <xsl:value-of select="$className"/>::<xsl:value-of select="&NAME;"/>
      <xsl:text>(</xsl:text>
      <xsl:call-template name="parameters"/>
      <xsl:text>)&#xa;</xsl:text>
      <xsl:text>{&#xa;</xsl:text>
      <xsl:for-each 
           select="$state/&SM;.StateVertex.outgoing/&SM;.Transition">
        <xsl:call-template name="generateStateBody">
          <xsl:with-param name="transition" 
           select="key('transKey',@xmi.idref)"/>
        </xsl:call-template>
      </xsl:for-each>
      <xsl:text>&INDENT2;context.errorMsg() ;&#xa;</xsl:text>
      <xsl:text>}&#xa;&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
  
  
  <xsl:template name="generateStateBody">
    <xsl:param name="transition"/>
    <xsl:text>&INDENT;if (cmd == </xsl:text>
    <xsl:variable name="eventId"
                           
select="$transition/&SM;.Transition.trigger/&SM;.SignalEvent/@xmi.idref"/>
    <xsl:value-of select="key('eventsKey',$eventId)/&NAME;"/>
    <xsl:if test="$transition/&SM;.Transition.guard">
      <xsl:text> &amp;&amp; </xsl:text>
      <xsl:value-of
     select="$transition/&GUARD;/*/Foundation.Data_Types.Expression.body"/>
      <xsl:text>()</xsl:text>
    </xsl:if>
    <xsl:text>)&#xa;</xsl:text>
    <xsl:text>&INDENT;{&#xa;</xsl:text>
    <xsl:text>&INDENT2;</xsl:text>
    <xsl:value-of 
         select="$transition/&SM;.Transition.effect/&ACTION;/*/&NAME;"/>
    <xsl:text>() ;&#xa;</xsl:text>
    <xsl:variable name="targetStateId" 
                 select="$transition/&SM;.Transition.target/*/@xmi.idref"/>
    <xsl:if test="$targetStateId != $transition/@xmi.id"/>
      <xsl:text>&INDENT2;cntx.changeState(</xsl:text>
      <xsl:value-of select="key('stateKey',$targetStateId)/&NAME;"/>
      <xsl:text>::instance());&#xa;</xsl:text>    
    <xsl:text>&INDENT;}&#xa;</xsl:text>
    <xsl:text>&INDENT;else&#xa;</xsl:text>
  </xsl:template>
  
  <!-- Generate function parameters -->
  <xsl:template name="parameters">
    <xsl:for-each select="&PARAM2;[&PARAM;.kind/@xmi.value != 'return']">
      <xsl:choose>
        <xsl:when test="&PARAMTYPE;">
          <xsl:value-of 
                select="key('dataTypeKey',&PARAMTYPE;/@xmi.idref)/&NAME;"/>
        </xsl:when>
        <xsl:when test="&PARAM;.type/&CLASS;">
          <xsl:value-of 
                 select="key('classKey',
                             &PARAM;.type/&CLASS;/@xmi.idref)/&NAME;"/>
          <xsl:text>&amp;</xsl:text>
        </xsl:when>
      </xsl:choose>
      <xsl:text>&#x20;</xsl:text>
      <xsl:value-of select="&NAME;"/>
      <xsl:if test="position() != last()">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
  
  <!-- Generate base classes -->
  <xsl:template name="baseClass">
    <xsl:if test="&GENERALIZATION;">
      <xsl:text> : </xsl:text>
      <xsl:for-each select="&GENERALIZATION;">
        <xsl:variable name="genAssoc" 
                      select="key('generalizationKey',@xmi.idref)"/>
        <xsl:text>public </xsl:text>
        <xsl:value-of 
     select="key('classKey',
                 $genAssoc/&SUPERTYPE;/&CLASS;/@xmi.idref)/&NAME;"/>
        <xsl:if test="position() != last()">
          <xsl:text>, </xsl:text>
        </xsl:if>
      </xsl:for-each>
    </xsl:if>
  </xsl:template>
  <xsl:template name="getStartState">
    <xsl:param name="classId"/>
    <xsl:variable name="stateMachine"
                  select="/&STATEMACH;[&SMC;/&CLASS;/@xmi.idref = 
                                                          $classId]"/>
    <xsl:value-of 
                  select="$stateMachine/&PSEUDOSTATE2;
                                          [&PSEUDOSTATE;.kind/@xmi.value = 'initial']/&NAME;"/>
  </xsl:template>
</xsl:stylesheet>

Here is a portion of the resulting C++ code:

//Forward declarations
class AnsweringMachine;
   
class AnsweringMachineState
{
public:
   
    virtual void doCmd(AnsweringMachine& cntx, Command cmd) = 0 ;
   
} ;
   
   
class Connected : public AnsweringMachineState
{
public:
   
    static Connected& instance() ;
   
private:
   
    Connected() {  } 
    Connected(const Connected&) {  } 
    void operator =(const Connected&) {  } 
    void doCmd(AnsweringMachine& cntx, Command cmd);
   
} ;
   
class HandlingMsg : public AnsweringMachineState
{
public:
   
    static HandlingMsg& instance() ;
   
private:
   
    HandlingMsg() {  } 
    HandlingMsg(const HandlingMsg&) {  } 
    void operator =(const HandlingMsg&) {  } 
    void doCmd(AnsweringMachine& cntx, Command cmd);
   
} ;
   
//!OTHER STATES WERE ELIDED TO SAVE SPACE... 
   
class AnsweringMachine
{
public:
   
    AnsweringMachine::AnsweringMachine();
   
    void doCmd(Command cmd);
    void changeState(AnsweringMachineState& newSate) ;
   
   
    bool moreMsgs();
    void playRemaining();
    void playCurrentMsg();
    void advanceAndPlayMsg();
    void deleteMsg();
    void sayGoodBye();
    void errorMsg();
   
private:
   
    AnsweringMachineState* m_State ;
   
} ;
   
void Connected::doCmd(AnsweringMachine& cntx, Command cmd)
{
    if (cmd =  = NEXT_MSG && cntx.moreMsgs())
    {
        cntx.playCurrentMsg() ;
        cntx.changeState(HandlingMsg::instance());
    }
    else
    if (cmd =  = NEXT_MSG && !cntx.moreMsgs())
    {
        cntx.playRemaining() ;
        cntx.changeState(Connected::instance());
    }
    else
    if (cmd =  = END)
    {
        cntx.sayGoodBye() ;
        cntx.changeState(GoodBye::instance());
    }
    else
        context.errorMsg() ;
}
   
//!OTHER STATES WERE ELIDED TO SAVE SPACE... 
   
//Constructor
AnsweringMachine::AnsweringMachine()
{
    //Initialize state machine in start state 
    m_State = &Start::instance() ;
}
   
void AnsweringMachine::changeState(AnsweringMachineState& newState)
{
    m_State = &newState;
}
   
void AnsweringMachine::doCmd(Command cmd)
{
    m_State->doCmd(*this, cmd);
}
   
bool AnsweringMachine::moreMsgs()
{
    //!TODO: Implement behavior of this action
}
   
void AnsweringMachine::playRemaining()
{
    //!TODO: Implement behavior of this action
}
   
void AnsweringMachine::playCurrentMsg()
{
    //!TODO: Implement behavior of this action
}
   
//!OTHER ACTIONS WERE ELIDED TO SAVE SPACE...

Discussion

You might want to write your own UML code generation for three reasons. First, you might not like the style or code substance generated by the modeling tool’s native code generator. Second, the tool might not generate code in the language you require. For instance, I do not know of a UML tool that generates Python.[4] Third, you want to automate the generation of a higher-level design pattern or software service that you use frequently in development. This last case is the motivation behind the solution section’s example.

Building a general-purpose, multi-language code-generation facility on top of XMI and XSLT is an interesting project, but well beyond the scope of a single example. The example’s solution is not intended as a production-ready generator, but it is a start. In particular, four important improvements could be made.

First, the generating XSLT should be broken down into small components that generate portions of the target programming language’s various constructs. This is similar to what we did in Chapter 9 with SVG generation.

Second, the generating XSLT should understand more UML information, such as access control, rather than hardcoding these decisions.

Third, the generator should use XSLT 1.0 extensions or XSLT 2.0 to generate code into multiple header and source files, rather than one monolithic source.

Fourth, but least important, several code-layout styles could be supported. For example, the C++ code is in the style of Allman, but many (misguided?) C++ programmers prefer K&R (http://www.tuxedo.org/~esr/jargon/html/entry/indent-style.html). Then again, you might just run the output through a separate code beautifier and dispense with all formatting decisions entirely.

12.9. Generating XSLT from XSLT

Problem

You want to generate XSLT from a different XML representation. Alternatively, you want to transform XSLT or pseudo-XSLT into real XSLT.

Solution

Two things about the control structure of XSLT sometimes annoy me. The first is the absence of an if-then-elsif-else construct; the second is the absence of a true looping construct. Of course, I am aware of xsl:choose and xsl:for-each, but each is lacking to some extent. I find xsl:choose annoying because the choose element serves practically no function, except to force an extra level of nesting. The xsl:for-each is not really a looping construct but an iteration construct. To emulate loops with counters, you have to use recursion or the Piez method (see Recipe 2.5), which is awkward.

This example illustrates an XSLT-to-XSLT generation by pretending that XSLT has the elements xslx:elsif, xslx:else, and xslx:loop. Since it really does not, you will create a stylesheet that generates true XSLT from the following pseudo-XSLT. Having an xsl:if and an xslx:if is awkward, but it would be wrong to use the standard XSLT namespace for your extended elements; these elements might be defined in standard XSLT someday:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xslx="http://www.ora.com/XSLTCookbook/ExtendedXSLT" >
   
<xsl:output method="text"/>
     
<xsl:template match="foo">
  <xslx:if test="bar">
    <xsl:text>You often will find a bar in the neighborhood of foo!</xsl:text>
  </xslx:if>
  <xslx:elsif test="baz">
    <xsl:text>A baz is a sure sign of geekdom.</xsl:text>
  </xslx:elsif>
  <xslx:else>
    <xslx:loop param="i" init="0" test="$i &lt; 10" incr="1">
      <xsl:text>Hmmm, nothing to say here but I'll say it 10 times.</xsl:text>
    </xslx:loop>
  </xslx:else>
  <xslx:loop param="i" init="10" test="$i >= 0" incr="-1">
    <xslx:loop param="j" init="10" test="$j >= 0" incr="-1">
      <xsl:text>&#xa;</xsl:text>
      <xsl:value-of select="$i * $j"/>
    </xslx:loop>
  </xslx:loop>
  <xslx:if test="foo">
       <xsl:text>foo foo! Nobody says foo foo!</xsl:text>
  </xslx:if>
  <xslx:else>
       <xsl:text>Well, okay then!</xsl:text>
  </xslx:else>
</xsl:template>
   
</xsl:stylesheet>

Here is a transformation that generates true XSLT from pseudo-XSLT. To keep things simple, this example does not include semantic checking such as checks for multiple xsl:else clauses with a single xsl:if or checks for duplication of parameters in nested loops:

<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xslx="http://www.ora.com/XSLTCookbook/ExtendedXSLT"
 xmlns:xso="dummy" >
   
<!-- Reuse the identity transform to copy -->
<!-- regular XSLT form source to destination -->
<xsl:import href="../util/copy.xslt"/>
   
<!-- DO NOT let the processor do the formatting via indent = yes -->
<!-- Because this could screw up xsl:text nodes                  -->
<xsl:output method="xml" version="1.0" encoding="UTF-8" />
   
<!--We use xso as a alias when we need to output literal xslt elements -->
<xsl:namespace-alias stylesheet-prefix="xso" result-prefix="xsl"/>
     
<xsl:template match="xsl:stylesheet | xsl:transform">
   <xso:stylesheet>
    <!--The first pass handles the if-elsif-else translation -->
    <!--and the conversion of xslx:loop to named template calls -->
    <xsl:apply-templates select="@* | node()"/>
    
    <!--The second pass handles the conversion of xslx:loop -->
    <!-- to recusive named templates -->
    <xsl:apply-templates mode="loop-body" select="//xslx:loop"/> 
    
  </xso:stylesheet>
</xsl:template>     
   
<!--We look for xslx:if's that have matching xslx:elsif or xslx:else -->
<xsl:template match="xslx:if[following-sibling::xslx:else or 
                             following-sibling::xslx:elsif]">
  <xsl:variable name="idIf" select="generate-id()"/>
  <xso:choose>
    <xso:when test="{@test}">
      <xsl:apply-templates select="@* | node()"/>
    </xso:when>
    <!-- We process the xsl:eslif and xslx:else in a special mode -->
    <!-- as part of the xsl:choose. We must make sure to only pick -->
    <!-- up the ones whose preceding xslx:if is this xslx:if -->
    <xsl:apply-templates 
     select="following-sibling::xslx:else[
                      generate-id(preceding-sibling::xslx:if[1]) = $idIf] | 
             following-sibling::xslx:elsif[
                      generate-id(preceding-sibling::xslx:if[1]) = $idIf]" 
     mode="choose"/>
  </xso:choose>
</xsl:template>
   
<!--Ignore xslx:elsif and xslx:else in normal mode -->
<xsl:template match="xslx:elsif | xslx:else"/>
   
<!--An xslx:elsif becomes a xsl:when -->
<xsl:template match="xslx:elsif"  mode="choose">
 <xso:when test="{@test}">
   <xsl:apply-templates select="@* | node()"/>
 </xso:when>
</xsl:template>
   
<!--An xslx:else becomes a xsl:otherwise -->
<xsl:template match="xslx:else" mode="choose">
 <xso:otherwise>
   <xsl:apply-templates/>
 </xso:otherwise>
</xsl:template>
   
   
<!-- An xslx:loop becomes a call to a named template -->
<xsl:template match="xslx:loop">
  <!-- Each template is given the name loop-N where N is position -->
  <!-- of this loop relative to previous loops at any level -->
  <xsl:variable name="name">
    <xsl:text>loop-</xsl:text>
    <xsl:number count="xslx:loop" level="any"/>
  </xsl:variable> 
  
  <xso:call-template name="{$name}">
    <xsl:for-each select="ancestor::xslx:loop">
      <xso:with-param name="{@param}" select="${@param}"/>
    </xsl:for-each>
    <xso:with-param name="{@param}" select="{@init}"/>
  </xso:call-template>  
 
</xsl:template>
   
<!-- Mode loop-body is used on the 2nd pass. -->
<!-- Here recursive templates are generated to do the looping.  -->
   
<xsl:template match="xslx:loop" mode="loop-body">
  <xsl:variable name="name">
    <xsl:text>loop-</xsl:text>
    <xsl:value-of select="position()"/>
  </xsl:variable> 
  
  <xso:template name="{$name}">
    <!--If this loop is nested in another it must -->
    <!--"see" the outer loop parameters so we generate these here -->
    <xsl:for-each select="ancestor::xslx:loop">
      <xso:param name="{@param}"/>
    </xsl:for-each>
    <!--The local loop parameter -->
    <xso:param name="{@param}"/>
    <!--Generate the recursion control test -->
    <xso:if test="{@test}">
      <!-- Apply template in normal mode to handle -->
      <!-- calls to nested loops while copying everything else. -->
      <xsl:apply-templates/>
      <!--This is the recursive call that applies -->
      <!--the incr to the loop param -->
      <xso:call-template name="{$name}">
        <xsl:for-each select="ancestor::xslx:loop">
          <xso:with-param name="{@param}" select="${@param}"/>
        </xsl:for-each>
        <xso:with-param name="{@param}" select="${@param} + {@incr}"/>
      </xso:call-template>
    </xso:if>
  </xso:template>
</xsl:template>
   
</xsl:stylesheet>

Here is the result of the generation:

<xso:stylesheet xmlns:xso="http://www.w3.org/1999/XSL/Transform" xmlns:xsl="http://
www.w3.org/1999/XSL/Transform" xmlns:xslx="http://www.ora.com/XSLTCookbook/
ExtendedXSLT" version="1.0">
  <xsl:output method="text"/>
  <xsl:template match="foo">
    <xso:choose>
      <xso:when test="bar">
        <xsl:text>You often will find a bar in the neighborhood of foo!</xsl:text>
      </xso:when>
      <xso:when test="baz">
        <xsl:text>A baz is a sure sign of geekdom.</xsl:text>
      </xso:when>
      <xso:otherwise>
        <xso:call-template name="loop-1">
          <xso:with-param name="i" select="0"/>
        </xso:call-template>
      </xso:otherwise>
    </xso:choose>
    <xso:call-template name="loop-2">
      <xso:with-param name="i" select="10"/>
    </xso:call-template>
    <xso:choose>
      <xso:when test="foo">
        <xsl:text>foo foo! Nobody says foo foo!</xsl:text>
      </xso:when>
      <xso:otherwise>
        <xsl:text>Well, okay then!</xsl:text>
      </xso:otherwise>
    </xso:choose>
  </xsl:template>
  <xso:template name="loop-1">
    <xso:param name="i"/>
    <xso:if test="$i &lt; 10">
      <xsl:text>Hmmm, nothing to say here but I'll say it 10 times.</xsl:text>
      <xso:call-template name="loop-1">
        <xso:with-param name="i" select="$i + 1"/>
      </xso:call-template>
    </xso:if>
  </xso:template>
  <xso:template name="loop-2">
    <xso:param name="i"/>
    <xso:if test="$i &gt;= 0">
      <xso:call-template name="loop-3">
        <xso:with-param name="i" select="$i"/>
        <xso:with-param name="j" select="10"/>
      </xso:call-template>
      <xso:call-template name="loop-2">
        <xso:with-param name="i" select="$i + -1"/>
      </xso:call-template>
    </xso:if>
  </xso:template>
  <xso:template name="loop-3">
    <xso:param name="i"/>
    <xso:param name="j"/>
    <xso:if test="$j &gt;= 0">
      <xsl:text>
</xsl:text>
      <xsl:value-of select="$i * $j"/>
      <xso:call-template name="loop-3">
        <xso:with-param name="i" select="$i"/>
        <xso:with-param name="j" select="$j + -1"/>
      </xso:call-template>
    </xso:if>
  </xso:template>
</xso:stylesheet>

Discussion

The xsl:namespace-alias element is the key to generating XSLT with XSLT. Without it, the processor would not be able to distinguish actual XSLT content from content that is meant to be output as literal result elements. Generation of XSLT with XSLT is useful in more contexts then this recipe will cover. Some additional examples include:

Facilitation of literate programming

Literate programming embeds code fragments in human-readable documentation (rather than the usual reverse situation) so that information is presented in the order that best suits people, rather than the order that best suits compilers.

Provision of conditional includes/imports

If this feature were in XSLT, it would probably require awkward extensions to the processing model akin to a C program’s preprocessor.

Enabling the dynamic evaluation of XPaths

This category refers to a stylesheet that generates XPaths from an import source and embeds them in another stylesheet that evaluates them statically. The extra level of indirection thus emulates dynamic behavior. Often those XPaths are embedded in a document. For example, you might see a table specification like:

<table of="person">
  <column label="Firstname" content="name/firstname" />
  <column label="Surname" content="name/surname" />
  <column label="Age" content="@age" type="number" />
  <sort select="Surname, Firstname, Age" /> 
</table>

It is easier to generate the table described by this XML—by generating XSLT from the table specification and then running that XSLT over the data—than it is to interpret the table specification within the same stylesheet you use to process the data.

See Also

See Oliver Becker’s XSLT loop compiler for a similar example that also validates (http://www.informatik.hu-berlin.de/~obecker/XSLT/#loop-compiler).



[1] Obviously, user input and output is also relevant. However, you can think of I/O in terms of messages.These user I/O messages are normally sent and received over different channels, though, not interprocess messages.

[2] I am tempted to add, only half in jest, that C++ is such a horrendously complex language that its developers are the most motivated to generate it rather than code it!

[3] Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, 1995).

[4] Not that I am a big fan of this curly-bracket-less language, but it has a large contingent of enthusiastic developers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.196.217