Алексей Валиков - Технология XSLT
<xsl:value-of select="$j"/> <xsl:value-of select="$j"/>
</xsl:variable> </xsl:variable>
<result> <result>
<xsl:copy-of select="$k"/> <xsl:copy-of select="$k"/>
</result> </result>
</xsl:if> </xsl:if>
</xsl:template> </xsl:template>
<xsl:variable name="gt"> <xsl:variable name="gt">
is greater than is greater than
</xsl:variable> </xsl:variable>
</xsl:stylesheet> </xsl:stylesheet>
В XSLT действует то же правило, что и во многих других языках программирования: нельзя дважды определять переменную с один и тем же именем. Однако и тут есть свои особенности.
□ Имена двух глобальных переменных могут совпадать в том и только том случае, когда они имеют разный порядок импорта. Например, если переменные с одинаковыми именами определены в разных преобразованиях, одно из них может быть импортировано. В этом случае переменная будет иметь значение, которое задано элементом xsl:variable со старшим порядком импорта.
□ Допускается совпадение имен локальной и глобальной переменных — в этом случае в области видимости локальной переменной будет использоваться локальное значение, в области видимости глобальной (но не локальной) — глобальное значение. Иными словами, локальные переменные "закрывают" значения глобальных.
□ Две локальные переменные могут иметь совпадающие имена в том и только том случае, если их области видимости не пересекаются.
Первое правило мы уже упоминали, когда разбирали порядок импорта: тогда мы сказали, что переменные со старшим порядком импорта переопределяют переменные с младшим порядком импорта. Это довольно важное обстоятельство, поскольку оно добавляет некоторые интересные возможности, но при этом также может породить скрытые ошибки.
ПримерПредположим, что в следующем преобразовании в шаблоне с именем choice мы генерируем два элемента input.
Листинг 5.24. Преобразование en.xsl<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="submit" select="'Submit'"/>
<xsl:variable name="reset" select="'Reset'"/>
<xsl:template name="choice">
<input type="button" value="{$submit}"/>
<xsl:text>
</xsl: text>
<input type="reset" value="{$reset}"/>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="choice"/>
</xsl:template>
</xsl:stylesheet>
Результатом этого преобразования будет следующий фрагмент:
<input type="button" value="Submit"/>
<input type="reset" value="Reset"/>
Для того чтобы перевести надписи на этих кнопках на другой язык достаточно просто переопределить переменные. Например, результатом выполнения следующего шаблона.
Листинг 5.25. Преобразование de.xsl<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="en.xsl"/>
<xsl:variable name="submit" select="'Senden'"/>
<xsl:variable name="reset" select="'Loeschen'"/>
</xsl:stylesheet>
будет тот же фрагмент, но уже на немецком языке:
<input type="button" value="Senden"/>
<input type="reset" value="Loeschen"/>
С другой стороны, переопределение переменных может быть и опасным: в случае, если отдельные модули разрабатывались независимо, совпадение имен глобальных переменных может привести к ошибкам. Для того чтобы такое было в принципе исключено, имя переменной следует объявлять в определенном пространстве имен, например:
<xsl:variable name="app:href" select="..."/>
<xsl:variable name="db:href" select="..."/>
В том случае, если префиксы app и db (которые, конечно же, должны быть объявлены) будут указывать на разные пространства имен, никакого конфликта между этими двумя переменными не будет.
Возвращаясь к теме совпадений имен переменных, продемонстрируем "скрытие" локальной переменной значения глобальной:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="i" select="1"/>
<xsl:template match="/">
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
<xsl:text>
</xsl:text>
<xsl:variable name="i" select="$i + 1"/>
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
</xsl:template>
</xsl:stylesheet>
Результатом выполнения этого шаблона будет:
i equals 1
i equals 2
Как можно видеть, объявление локальной переменной i "скрыло" значение глобальной переменной i. Более того, это преобразование любопытно еще и тем, что локальная переменная объявляется через глобальную — такое тоже допускается.
Рассмотрим теперь случай двух локальных переменных. Попробуем объявить две локальные переменные — одну за другой в следующем шаблоне:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="i" select="1"/>
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
<xsl:text>
</xsl:text>
<xsl:variable name="i" select="2"/>
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
</xsl:template>
</xsl:stylesheet>
В тексте этого шаблона мы выделили две области видимости двух переменных: серой заливкой — область видимости первой и полужирным шрифтом — область действия второй переменной. Вследствие того, что эти области пересекаются, шаблон будет некорректным — процессор выдаст сообщение об ошибке вида:
Failed to compile style sheet
At xsl:variable on line 9 of file stylesheet.xsl:
Variable is already declared in this template
Приведем теперь другое преобразование, в котором элементы xsl:variable принадлежат двум братским элементам:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="i" select="1"/>
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
<xsl:variable name="i" select="2"/>
<xsl:text>i equals </xsl:text>
<xsl:value-of select="$i"/>
</xsl:template>
</xsl:stylesheet>
В этом случае никакого пересечения областей видимости нет, поэтому и ошибкой наличие в одном шаблоне двух локальных переменных с одинаковыми именами тоже не будет. Приведенное выше преобразование возвратит результат вида:
i equals 1
i equals 2
Использование переменных
Как правило, первой реакцией на известие о том, что переменные в XSLT нельзя изменять является реплика: "Да зачем они вообще тогда нужны?!".
Претензия со стороны процедурного программирования вполне серьезна и обоснованна — изменяемые переменные являются большим подспорьем в сложных многошаговых вычислениях. Тем не менее, переменные, даже в том виде, в каком они присутствуют в XSLT, являются очень важным и полезным элементом языка. Ниже мы перечислим наиболее типичные случаи использования переменных.
□ Переменные могут содержать значения выражений, которые много раз используются в преобразовании. Это избавит процессор от необходимости пересчитывать выражение каждый раз по-новому.
□ Переменной может присваиваться результат преобразования, что позволяет манипулировать уже сгенерированными частями документа.
□ Переменные могут использоваться для более прозрачного доступа к внешним документам.
ПримерыПервый случай использования совершенно очевиден: если в преобразовании многократно используется какое-либо сложное для вычисления или просто громоздкое для записи выражение, переменная может служить для сохранения единожды вычисленного результата. Например, если мы много раз обращаемся ко множеству ссылок данного документа посредством выражения вида:
//a[@href]
гораздо удобней и экономней с точки зрения вычислительных ресурсов объявить переменную вида
<xsl:variable name="links" select="//a[@href]"/>
и использовать ее в преобразовании как $a. Фильтрующие выражения языка XPath (продукция FilterExpr) позволяют затем обращаться к узлам выбранного множества так же, как если бы мы работали с изначальным выражением. Например, $a[1] будет эквивалентно //a[@href][1], a $a/@href — выражению //a[@href]/@href. При этом при обращении к $a процессор не будет заново искать все элементы a с атрибутом href, что, по всей вероятности, положительно скажется на производительности.