Книга: Технология XSLT

Область видимости переменных

Область видимости переменных

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

? Областью видимости глобальной переменной является все преобразование, то есть значение переменной, объявленной элементом верхнего уровня, может быть использовано в преобразовании где угодно. К такой переменной можно обращаться даже до ее объявления, единственным ограничением является то, что переменная не должна определяться через собственное значение — явно или неявно.

? Локальную переменную можно использовать только после ее объявления и только в том же родительском элементе, которому принадлежит объявляющий элемент xsl:variable. В терминах XPath область видимости локальной переменной будет определяться выражением

following-sibling:node()/descendant-or-self:node().

Для того чтобы до конца прояснить ситуацию, приведем несколько примеров.

Предположим, что мы определяем переменную с именем ID и значением 4 следующим образом:

<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 ...
 <xsl:variable name="ID" select="4"/>
 ...
</xsl:stylesheet>

Несложно видеть, что здесь мы определили глобальную переменную, а значит, ее значение можно использовать в преобразовании в любом месте. Например, мы можем определить через нее другие глобальные переменные, либо использовать в шаблоне:

<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 ...
 <xsl:variable name="leaf" select="//item[@id=$ID]"/>
 <xsl:variable name="ID" select="4"/>
 <xsl:variable name="path" select="$leaf/ancestor-or-self::item"/>
 ...
</xsl:stylesheet>

Причем, как уже было сказано, глобальная переменная может быть использована и до объявления: в нашем случае переменная leaf определяется через переменную ID, a path — через leaf. Конечно же, не следует забывать и то правило, что переменные не могут объявляться посредством самих себя, явно или неявно. Очевидно, что объявление:

<xsl:variable name="ID" select="$ID - 1"/>

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

<xsl:variable name="ID" select="$id — 1/>
<xsl:variable name="id" select="$ID + 1"/>

поскольку переменная ID определяется через переменную id, которая определяется через переменную ID и так до бесконечности.

Дела с локальными переменными обстоят чуть-чуть сложнее. Для того чтобы объяснить, что же такое область видимости, обратимся к следующему преобразованию.

Листинг 5.22. Преобразование, использующее переменные i, j, k и gt

<xsl:stylesheet
 version="1.0"
 xmlns:xsl="... ">
 <xsl:template match="/">
  <xsl:variable
   name="i"
   select="2"/>
  <xsl:variable
   name="j"
   select="$i - 1"/>
  <xsl:if test="$i > $j">
   <xsl:variable name="k">
    <xsl:value-of select="$i"/>
    <xsl:value-of select="$gt"/>
    <xsl:value-of select="$j"/>
   </xsl:variable>
   <result>
    <xsl:copy-of select="$k"/>
   </result>
  </xsl:if>
 </xsl:template>
 <xsl:variable name="gt">
  is greater than
 </xsl:variable>
</xsl:stylesheet>

В этом преобразовании определены три локальные переменные — i, j и k и одна глобальная переменная — gt. На следующих четырех листингах мы выделим серой заливкой область видимости переменной (то есть область, где ее можно использовать), а само определение переменной отметим полужирным шрифтом.

Листинг 5.23. Области видимости переменных i, j, k и gt

Область видимости переменной i               Область видимости переменной j
<xsl:stylesheet                  <xsl:stylesheet
 version="1.0" xmlns:xsl="... ">  version="1.0" xmlns:xsl="... ">
 <xsl:template match="/">         <xsl:template match="/">
  <xsl:variable name="i"           <xsl:variable name="i"
   select="2"/>                     select="2"/>
  <xsl:variable name="j"           <xsl:variable name="j"
   select="$i - 1"/>                select="$i - 1"/>
  <xsl:if test="$i > $j">          <xsl:if test="$i > $j">
   <xsl:variable name="k">          <xsl:variable name="k">
    <xsl:value-of select="$i"/>      <xsl:value-of select="$i"/>
    <xsl:value-of select="$gt"/>     <xsl:value-of select="$gt"/>
    <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>

Область видимости переменной k               Область видимости переменной gt
<xsl:stylesheet                  <xsl:stylesheet
 version="1.0" xmlns:xsl="... ">  version="1.0" xmlns:xsl="... ">
 <xsl:template match="/">         <xsl:template match="/">
  <xsl:variable name="i"           <xsl:variable name="i"
   select="2"/>                     select="2"/>
  <xsl:variable name="j"           <xsl:variable name="j"
   select="$i - 1"/>                select="$i - 1"/>
  <xsl:if test="$i > $j">          <xsl:if test="$i > $j">
   <xsl:variable name="k">          <xsl:variable name="k">
    <xsl:value-of select="$i"/>      <xsl:value-of select="$i"/>
    <xsl:value-of select="$gt"/>     <xsl:value-of select="$gt"/>
    <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>&#xA;</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>&#xA;</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>&#xA;</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="/">
  <p>
   <xsl:variable name="i" select="1"/>
   <xsl:text>i equals </xsl:text>
   <xsl:value-of select="$i"/>
  </p>
  <p>
   <xsl:variable name="i" select="2"/>
   <xsl:text>i equals </xsl:text>
   <xsl:value-of select="$i"/>
  </p>
 </xsl:template>
</xsl:stylesheet>

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

<p>
 i equals 1
</p>
<p>
 i equals 2
</p>

Оглавление книги


Генерация: 1.817. Запросов К БД/Cache: 2 / 0
поделиться
Вверх Вниз