Главная » XSLT » Генерация оберток для данных

0

Задача

Требуется создать классы, которые надстраивали бы над заключенными в со­общение данными интерфейс, безопасный с точки зрения типов.

Решение

Показанная ниже таблица стилей работает в двух режимах. Если в качестве параметра передано имя сообщения, то генерируется обертка только для этого со­общения. В противном случае генерируются обертки для всех сообщений.

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="text"/> <xsl:strip-space elements="*"/>

<!– Сообщение, для ^^poTo генеpиpoвaть oбеpткy. ‘*’ — для всех –> <xsl:param name="message" select=" ‘*’ "/> <!– Каталог, куда помещать сгенеpиpoвaнные файлы –> <xsl:param name="generationDir" select=" ‘src/’ "/>

<!– Расширение имени заголовочного файла    C++ –>

<xsl:param name="headerExt" select=" ‘.h’    "/>

<!– Расширение имени исходного файла C++    –>

<xsl:param name="sourceExt" select=" ‘.C’    "/>

<!– Ключ для поиска типов данных по имени –> <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="/"> <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>

<!– Если тип данных, представленный сообщением, описан в репозитории, сгенерировать для него заголовочный и исходный файл –> <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>

<!– Заголовки генерируются только, если тип данных в сообщении – Structure. Возможен также тип данных XML. Если полезная нагрузка представлена в формате XML, обертка не генерируется. –> <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>s#xa;</xsl:text> </xsl:for-each> <xsl:text>&#xa;</xsl:text> private:<xsl:text>s#xa;s#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>s#xa;</xsl:text> </xsl:for-each> } ;

</xsl:document> </xsl:template>

<!– Исходные файлы генерируются только, если тип данных в сообщении – Structure. Возможен также тип данных XML. Если полезная нагрузка представлена в формате XML, обертка не генерируется. –> <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>

<!– Предполагается, что члены, которые сами являются структурами, –> <!– возвращаются по ссылке. –>

<xsl:template match="Structure" mode="returnType"> const <xsl:value-of select="Name"/>&amp;<xsl:text/> </xsl:template>

<!– Примитивы, которые можно представить встроенными в C++ типами, — <!– отображаются на эти типы. В противном случае предполагаем, что — <!– примитив определен во внешнем файле. –> <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>

Этот генератор порождает только методы get, но при необходимости его лег­ко можно расширить так, что будут создавать методы set и любые другие. Вот пример сгенерированного заголовочного файла:

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

} ;

А вот как выглядит cpp-файл:

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

}

Обсуждение

В этом разделе термином обертка обозначается класс, предоставляющий объектно-ориентированный интерфейс к данным, которые на языке C записыва­ются в виде структуры struct. Когда-то мне довелось работать над проектом, где все обертки сообщений кодировались вручную. Работа была утомительной, но результат того стоил. Возьмем, к примеру, сообщение, содержащее цену, количе­ство и дату. Все эти высокоуровневые типы можно представить типом int. Если по ошибке использовать в программе одно вместо другого, компилятор ничего не заметит. Обертки позволяют надстроить над данными сообщения тонкий проме­жуточный слой, который преобразует низкоуровневое представление в примити­вы типа класса, например: Price, Qty и Date. Автоматическая генерация оберток дает возможность сделать это без лишних усилий.

Репозиторий сообщений и XSLT-генератор позволяют автоматизировать про­цесс создания оберток. На практике обертки иногда содержат дополнительную логику, для генерации которой нужно включить в репозиторий добавочные мета­данные. Типичный случай возникает, когда данные сообщения содержат масси­вы. Часто имеется еще специальное поле, в котором хранится количество элемен­тов в массиве. Если в обертку вручную включается код добавления элемента в массив, то придется взять значение этого поля, которое дает индекс следующей незаполненной ячейки, и увеличить его после помещения новых данных. Сгене­рировать такой год можно лишь в том случае, если в репозитории есть информа­ция о том, с каким массивом ассоциировано поле размера.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии