Главная » XSLT » Автоматизация вставки отладочной печати

0

Задача

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

Решение

Оливер Беккер (Oliver Becker) разработал полезное преобразование, которое принимает на входе любую таблицу стилей, а на выходе возвращает ту же табли­цу, дополненную трассировкой:

<!–

Утилита трассировки, модифицирует таблицу стилей, добавляя трассировочные сообщения Версия 0.2 GPL (c) Oliver Becker, 2002-02-13

obecker@informatik.hu-berlin.de

– >

<xsl:transform version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:trace="http://www.obqo.de/XSL/Trace" xmlns:alias="http://www.w3.org/TransformAlias" exclude-result-prefixes="alias">

<xsl:namespace-alias stylesheet-prefix="alias" result-prefix="xsl" /> <!– <xsl:output indent="yes" /> –> <!– Корневой элемент XSLT –>

<xsl:template match="xsl:stylesheet | xsl:transform"> <xsl:copy>

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

<xsl:copy-of select="document(”)/*/namespace::trace" />

<!– ditto: быть может, пространство имен использовалось только —>

<!– в качестве значения атрибута –>

<xsl:copy-of select="namespace::*|@*" />

<xsl:apply-templates />

<!– добавить служебные шаблоны –>

<xsl:copy-of

select="document(”)/*/xsl:template

[@mode=’trace:getCurrent’ or @name=’trace:getPath’]" /> <!– вычислить минимальный приоритет и добавить шаблон –> <!– по умолчанию с меньшим приоритетом для узлов-элементов –> <xsl:variable name="priority"

select="xsl:template/@priority

[not(. &gt; current( )/xsl:template/@priority)]" /> <xsl:variable name="newpri"> <xsl:choose>

<xsl:when test="$priority &lt; -1">

<xsl:value-of select="$priority – 1" /> </xsl:when>

<!– на случай, если есть только более высокие приоритеты –> <!– или их нет вовсе –> <xsl:otherwise>-2</xsl:otherwise> </xsl:choose> </xsl:variable>

<!– копируем только содержимое –> <alias:template match="*" priority="{$newpri}"> <xsl:copy-of select="document(”)/*/xsl:template

[@name=’trace:defaultRule’]/node( )" />

</alias:template> </xsl:copy> </xsl:template>

<!– шаблоны XSLT –> <xsl:template match="xsl:template"> <xsl:copy> <xsl:copy-of select="@*" /> <!– первое: копируем параметры — > <xsl:apply-templates select="xsl:param" /> <alias:param name="trace:callstack" /> <xsl:choose>

<xsl:when test="@name">

<alias:variable name="trace:current"

select="concat($trace:callstack,’/{@name}’)" />

</xsl:when> <xsl:otherwise>

<alias:variable name="trace:current" select="concat($trace:callstack,

‘/{count(preceding-sibling::xsl:template)+1}’)" /> </xsl:otherwise> </xsl:choose>

<!– вывести сообщение –> <alias:message>

<alias:call-template name="trace:getPath" /> <alias:text>&#xA; stack: </alias:text> <alias:value-of select="$trace:current" /> <xsl:if test="@match or @mode"> <alias:text> (</alias:text> <xsl:if test="@match">

<alias:text>match="<xsl:value-of select="@match" />"

</alias:text>

<xsl:if test="@mode">

<alias:text><xsl:text> </xsl:text></alias:text> </xsl:if> </xsl:if>

<xsl:if test="@mode">

alias:text>mode="<xsl:value-of select="@mode" />"</alias:text> </xsl:if>

<alias:text>)</alias:text> </xsl:if>

<xsl:apply-templates select="xsl:param" mode="traceParams" /> </alias:message>

<!– обработать дочерние элементы, за исключением параметров –> <xsl:apply-templates select="node( )[not(self::xsl:param)]" /> </xsl:copy> </xsl:template>

<!– добавить параметр callstack для команд apply-templates и call-template — >

<xsl:template match="xsl:apply-templates | xsl:call-template"> <xsl:copy>

<xsl:copy-of select="@*" />

<alias:with-param name="trace:callstack" select="$trace:current" /> <xsl:apply-templates /> </xsl:copy> </xsl:template>

<!– выводим значения пapaметpoв –>

<xsl:template match="xsl:param" mode="traceParams">

<alias:text>&#xA; param: name="<xsl:value-of select="@name" />’ value="</alias:text>

<alias:value-of select="${@name}" />" <alias:text /> <!–

<alias:copy-of select="${@name}" />" <alias:text /> — >

</xsl:template>

<!– выводим значения пеpеменных –> <xsl:template match="xsl:variable"> <xsl:copy>

<xsl:copy-of select="@*" /> <xsl:apply-templates /> </xsl:copy>

<xsl:if test="ancestor::xsl:template">

<alias:message> variable: name="<xsl:value-of select="@name"

value="<alias:text /> <alias:value-of select="${@name}" />" </alias:message> </xsl:if> </xsl:template>

<!– кoпиpyем все неoбpaбoтaнные узлы –> <xsl:template match="*|@*"> <xsl:copy>

<xsl:apply-templates select="@*" /> <xsl:apply-templates /> </xsl:copy>

</xsl:template>

<!– ********************************************** — >

<!– Следующие шаблоны коп^уются в мoдифициpoвaннyю —>

<!– таблицу стилей                            –>

<!– *********************************************** — >

<!– | trace:getPath

| вычислить абсолютный путь к контекстному узлу +–>

<xsl:template name="trace:getPath"> <xsl:text>node: </xsl:text> <xsl:for-each select="ancestor::*"> <xsl:value-of

select="concat(‘/’, name( ), ‘[‘,

count(preceding-sibling::*[name( )=name(current( ))])+1, ‘]’)" /> </xsl:for-each>

<xsl:apply-templates select="." mode="trace:getCurrent" /> </xsl:template>

<!–

| trace:getCurrent

| вычислить последний шаг пути доступа в зависимости

| от типа узла +–>

<xsl:template match="*" mode="trace:getCurrent"> <xsl:value-of

select="concat(‘/’, name( ), ‘[‘,

count(preceding-sibling::*[name( )=name(current( ))])+1, ‘]’)" /> </xsl:template>

<xsl:template match="@*" mode="trace:getCurrent"> <xsl:value-of select="concat(‘/@’, name( ))" /> </xsl:template>

<xsl:template match="text( )" mode="trace:getCurrent"> <xsl:value-of

select="concat(‘/text( )[‘, count(preceding-sibling::text( ))+1,

‘]’)" />

</xsl:template>

<xsl:template match="comment( )" mode="trace:getCurrent"> <xsl:value-of

select="concat(‘/comment( )[‘,

count(preceding-sibling::comment( ))+1, ‘]’)" />

</xsl:template>

<xsl:template match="processing-instruction( )" mode="trace:getCurrent"> <xsl:value-of

select="concat(‘/processing-instruction( )[‘,

count(preceding-sibling::processing-instruction( ))+1, ‘]’)" />

</xsl:template> <!–

| trace:defaultRule

| правило по умолчанию с передачей параметров +–>

<xsl:template name="trace:defaultRule"> <xsl:param name="trace:callstack" /> <xsl:message>

<xsl:call-template name="trace:getPath" /> <xsl:text>&#xA; default rule applied</xsl:text> </xsl:message> <xsl:apply-templates>

<xsl:with-param name="trace:callstack" select="$trace:callstack" /> </xsl:apply-templates> </xsl:template>

</xsl:transform>

Обсуждение

Ниже приведен пример отладочной выдачи, получившейся в результате применения этого преобразования к таблице стилейpostorder.orgchart.xslt из ре­цепта 5.5:

node: /employee[1]

stack: /1 (match="/employee") node: /employee[1]/employee[1]

stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[1]

stack: /1/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2]

stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[1]/employee[2]/employee[1]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[1]/employee[2]

stack: /1/2/2/reportsTo node: /employee[1]/employee[1]/employee[2]

stack: /1/2/2/HimHer node: /employee[1]/employee[1]

stack: /1/2/reportsTo node: /employee[1]/employee[1]

stack: /1/2/HimHer node: /employee[1]/employee[2]

stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[1]

stack: /1/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]

stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[2]/employee[2]/employee[1]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[2]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]/employee[3]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[2]/employee[2]

stack: /1/2/2/reportsTo node: /employee[1]/employee[2]/employee[2]

stack: /1/2/2/HimHer node: /employee[1]/employee[2]

stack: /1/2/reportsTo node: /employee[1]/employee[2]

stack: /1/2/HimHer node: /employee[1]/employee[3]

stack: /1/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1]

stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[1]/employee[1]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[2]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]/employee[3]

stack: /1/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[1]

stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[1]

stack: /1/2/2/HimHer node: /employee[1]/employee[3]/employee[2]

stack: /1/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]

stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]/employee[1]

stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[1]

stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[1]

stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[2]

stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[1]

stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[2]

stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]

stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[2]

stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[3]

stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[3]/employee[1]

stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[3]

stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[3]

stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]

stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]

stack: /1/2/2/HimHer node: /employee[1]/employee[3]

stack: /1/2/reportsTo node: /employee[1]/employee[3]

stack: /1/2/HimHer node: /employee[1]

stack: /1/reportsTo node: /employee[1] stack: /1/HimHer

Модифицированная таблица стилей выводит трассировочные сообщения с помощью команды xsl:message. Для каждого обработанного узла печатается следующая информация:

node: [XPath to this node]

stack: [состояние стека вызовов в момент обращения к данному шаблону] param: name="[имя параметра]" value="[значение параметра]" more parameters …

variable: name="[имя переменной]" value="[значение переменной]" еще переменные …

Стек вызовов распечатывается в виде пути (с разделителем /) и включает все вызванные к этому моменту шаблоны. Если у шаблона есть атрибут name, то печа­тается его значение. В противном случае шаблон обозначается в стеке вызовов числом (позицией). Если у текущего шаблона нет имени, печатается значение ат­рибута match. Если задан атрибут mode, он тоже печатается.

Существует известная проблема – при обработке параметров и переменных выводится их строковое значение (результат обращения к xsl:value-of). Но это бессмысленно для наборов узлов и фрагментов результирующего дерева. А применение xsl:copy-of дает ошибку, если переменная содержит узлы атри­бутов или пространств имен без родителей.

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

По теме:

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