Главная » XSLT » Генерация определений констант

0

Задача

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

Решение

Можно построить преобразование на язык C++, которое легко адаптируется для языков C, C# и 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="*"/>

<!– Имя выходного заголовочного файла. –> <xsl:param name="file" select=" ‘MESSAGE_IDS.h’ "/>

<!– По умолчанию генерируются константы в синтаксисе C++ –> <xsl:variable name="constants-type" select=" ‘const int’ "/>

<!– По умолчанию используется оператор присваивания C++ –> <xsl:variable name="assignment" select=" ‘ = ‘ "/>

<!– По умолчанию используется символ конца предложения C++ –> <xsl:variable name="terminator" select=" ‘;’ "/>

<!– Преобразовать репозиторий в последовательность определений констант, соответствующих сообщениям –> <xsl:template match="MessageRepository">

<xsl:call-template name="constants-start"/> <xsl:apply-templates select="Messages/Message"/> <xsl:call-template name="constants-end"/> </xsl:template>

<!– Каждое сообщение превращается в комментарий и определение константы –>

<xsl:template match="Message">

<xsl:apply-templates select="." mode="doc" /> <xsl:apply-templates select="." mode="constant" /> </xsl:template>

<!– Заголовочные файлы в C++ начинаются с директив, предотвращающих –> <!– повторное включение (стражи) –> <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++ заканчиваются директивой, закрывающей –> <!– директиву-страж в начале –>

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

<!— Определению каждой константы предшествует комментарий, описывающий —> <!– соответствующее ей сообщение –> <xsl:template match="Message" mode="doc"> /*

*       Назначение: <xsl:call-template name="format-comment">

<xsl:with-param name="text" select="Documentation"/> </xsl:call-template>

*       Формат данных: <xsl:value-of select="DataTypeName"/>

*       Отправитель: <xsl:apply-templates select="Senders" mode="doc"/>

*            Получатель: <xsl:apply-templates select="Receivers" mode="doc"/> */

</xsl:template>

<!– Используется для генерации документации по сообщениям. Перечисляет процессы-отправители и получатели –> <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>

<!– Этот шаблон разбивает комментарии на строки длиной 40 символов –> <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,4 0)"/>

</xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template>

<!– Каждое сообщение пpевpaщaется в константу, значение кoтopoй совпадает с идентификaтopoм сообщения –> <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>

<!– Игнopиpoвaть текстовые узлы, не oбpaбoтaнные явно пpедыдyщими шаблонами –>

<xsl:template match="text( )"/> </xsl:stylesheet>

Применив эту таблицу стилей к нашему репозиторию, получим такой код:

#ifndef MESSAGE_IDS_h

#define MESSAGE_IDS_h /*

*                          Назначение:    Зaпpoс на добавление нового заказа.

*                          Фopмaт данных: AddStockOrderData

*                          Отпpaвитель:   StockClient

*                          Получатель:    StockServer */

const int ADD_STOCK_ORDER_ID = 1; /*

*                          Назначение:    Пoдтвеpждение успешного добавления

*             нового заказа.

*                          Фopмaт данных: AddStockOrderAckData

*                          Отпpaвитель:   StockServer

*             Получатель:       StockClient */

const int ADD_STOCK_ORDER_ACK_ID = 2;

/*

*                Назначение:    Сообщение об ошибке при добавлении

*                 нового заказа.

*                Формат данных: AddStockOrderNackData

*                Отправитель:   StockServer

*                 Получатель:    StockClient */

const int ADD_STOCK_ORDER_NACK_ID = 3; //И т.д. …

#endif /* MESSAGE_IDS_h */

Обсуждение

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

?               Кодируйте языково-зависимые конструкции в параметрах верхнего уров­ня или в переменных, чтобы их легко можно было переопределить в импор­тирующей таблице стилей или (если вы решите остановиться на парамет­рах) передать извне при запуске таблицы.

?               Помещайте генерацию различных компонентов в отдельные шаблоны, ко­торые можно будет переопределить в импортирующей таблице.

Спроектировав преобразование таким образом, легко с минимальными изменения­ми получить определение констант в стиле C в виде директив препроцессора #define:

<?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 все определения должны находиться внутри класса, но и этого легко добиться:

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

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

По теме:

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