Главная » XSLT » Генерация предложения switch

0

Задача

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

Решение

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

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

<!– Генерируем пролог исходного файла –> <xsl:call-template name="file-start"/>

<!– Генерируем директивы include для включения файлов, –>

<!– в которых описываются сообщения, принимаемые этим процессом —>

<xsl:apply-templates select="Messages/

Message[Receivers/

ProcessRef = $process or $process = ‘*’]"

mode="includes"/>

<!– Генеpиpyем пpoлoг пpедлoжения switch –> <xsl:call-template name="switch-start"/>

<!– Генеpиpyем тело пpедлoжения switch –> <xsl:apply-templates select="Messages/

Message[Receivers/

ProcessRef = $process or $process = ‘*’]"

mode="switch"/>

<!– Генеpиpyем эпилог пpедлoжения switch –> <xsl:call-template name="switch-end"/>

<!– Генеpиpyем эпилог файла –> <xsl:call-template name="file-end"/> </xsl:template>

<!– Генеpиpyем диpективы include для отдельных сообщений –> <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>

<!— Генеpиpyем ветвь case для одного типа сообщений —> <xsl:template match="Message" mode="switch"> case <xsl:value-of select="Name"/>_ID:

<xsl:call-template name="case-action"/> </xsl:template>

<!– Генеpиpyем действие – вызов oбpaбoтчикa сообщения –> <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>

<!– По умолчанию пустышки. Пpи необходимости можно пеpеoпpеделить –>

<xsl:template name="file-start"/> <xsl:template name="file-end"/>

<!– Генерируем пролог предложения switch –> <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>

<!– Генерируем сигнатуру точки входа в обработчик сообщения –> <xsl:template name="process-function"> bool processMessage(const Message&amp; msg) </xsl:template>

</xsl:stylesheet>

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

#include <messages/ADD_STOCK_ORDER.h> #include <messages/CANCEL_STOCK_ORDER.h>

#include <transport/Message.h> #include <transport/MESSAGE_IDS.h>

bool processMessage(const Messages 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 ;

}

}

Обсуждение

В приложениях, которые обрабатывают сообщения, всегда имеется какое-то ветвление по типу сообщений. Конструкции могут различаться, но как правило проверяется идентификатор сообщения, и в зависимости от его значения сооб­щение направляется тому или иному обработчику. Идентификатор может быть целым числом (как в данном случае) или строкой. Обработчик же может быть простой функцией или объектом, создаваемым специально для обработки сооб­щения. Даже совсем простые обработчики важно делать модульными, чтобы ими можно было воспользоваться в разных контекстах. В компании, где я когда- то работал, группа программистов решила создать очень интересную систему на языке Perl, которая генерировала бы С++-код из XSD-схемы. Она оказалась на­столько полезной, что ей захотели пользоваться и другие группы. К сожалению, разработчики задумывали систему только для своих нужд, поэтому во всех ос­тальных отношениях замечательное решение оказалось непригодным для по­вторного использования. Написание даже простейшего генератора кода – слож­ная задача, поэтому стремитесь сделать его максимально общим, чтобы другим не пришлось заново преодолевать все трудности.

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

Можно ли написать единственный генератор, достаточно общий для обра­ботки всех вариантов? Может быть, но, скорее всего, он окажется чрезвычайно сложным. Лучше создать компоненты, которыми можно пользоваться в слож­ных генераторах. Вот, например, обобщенный генератор предложения switch в стиле C:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xslt [

<!– Служит для управления отступами –>

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

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

<!– Вызывает стандартный генератор тела ветви case. –> <!– Переопределить, если нужно сделать что-то особое для –> <!– ветви default. –>

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

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

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

По теме:

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