Алексей Валиков - Технология XSLT
Элемент xsl:apply-imports применяет шаблоны точно так же, как и элемент xsl:apply-templates, но при этом он имеет две особенности.
□ Шаблоны, определенные в основном преобразовании, применяться не будут, поскольку xsl:apply-imports применяет только импортированные правила.
□ Элемент xsl:apply-imports применяет только те правила, режим (mode) которых совпадает с режимом текущего шаблона.
В текущей версии XSLT xsl:apply-imports не может вызывать импортированные именованные шаблоны.
Для того чтобы лучше понять, зачем нужна такая сложная схема импорта, проведем аналогию с объектно-ориентированным программированием. Если рассматривать правила преобразований как методы классов, то импорт преобразований будет ни чем иным, как наследованием — все методы (шаблоны) класса-потомка (импортируемого преобразования) будут доступны в классе-наследнике (импортирующем преобразовании). При этом класс-наследник может переопределить методы класса потомка (шаблоны основного преобразования имеют порядок импорта старше, чем шаблоны импортированного преобразования). В этой схеме использование элемента xsl:apply-imports будет равносильно вызову метода родительского класса вместо переопределенного метода класса потомка.
Приведем пример Java-классов, которые будут аналогичны преобразованиям home.xsl и base.xsl.
Листинг 5.20. Класс homepublic class home {
public String home() {
return "<a href="http://www.xsltdev.ru">www.xsltdev.ru</a>";
}
}
Листинг 5.21. Класс basepublic class base extends home {
public String home() {
return ("Visit " + super.home());
}
}
В этом примере вызов родительского метода super.home() соответствует применению элементом xsl:apply-imports импортированного шаблона.
Тело шаблона
Фактически, элемент xsl:template, определяющий шаблонное правило, задает не более чем условия, при которых это правило должно выполняться. Конкретные же действия и инструкции, которые должны быть исполнены, определяются содержимым элемента xsl:template и составляют тело шаблона.
ПримерТело следующего шаблона (выделенное полужирным шрифтом):
<xsl:template match="page">
<body>
<xsl:value-of select="."/>
</body>
Комментарии
<xsl:copy-of select="comment()"/>
</xsl:template>
состоит из литерального элемента body, текстового узла и элемента xsl:copy-of. При выполнении этого шаблона элемент body будет выведен как есть (при этом содержимое его будет вычислено); текстовый узел будет скопирован в выходящее дерево; элемент xsl:copy-of будет заменен множеством дочерних комментариев текущего узла.
Следует заметить, что текстовый узел в данном случае состоит не только из строки "Комментарии". Он включает также все пробельные символы и символы переноса строки.
Тело шаблона может быть также и пустым. В этом случае результат обработки узлов, соответствующих этому шаблону будет пустым. Например, если в преобразовании содержится шаблон вида
<xsl:template match="comment()"/>
то каждый раз, встретив узел комментария и обрабатывая его этим правилом, процессор будет получать пустой результат. Таким образом, используя пустые шаблоны совместно с идентичным преобразованием можно добиться эффекта удаления определенных узлов из шаблона, как это было показано ранее в этой главе.
Тело шаблона может содержать любые текстовые узлы, комментарии, инструкции по обработке и литеральные элементы результата при условии, что не будет нарушен синтаксис XML-документа. Тело шаблона может также содержать следующие элементы XSLT, называемые также инструкциями (не путать с инструкциями по обработке):
□ xsl:apply-imports;
□ xsl:apply-templates;
□ xsl:attribute;
□ xsl:call-template;
□ xsl:choose;
□ xsl:comment;
□ xsl:copy;
□ xsl:copy-of;
□ xsl:element;
□ xsl:fallback;
□ xsl:for-each;
□ xsl:if;
□ xsl:message;
□ xsl:number;
□ xsl:param;
□ xsl:processing-instruction;
□ xsl:text;
□ xsl:value-of;
□ xsl:variable.
Элементы xsl:param и xsl:variable, которые входят в этот список в преобразовании, могут быть как элементами верхнего уровня, так и инструкциями. В первом случае они определяют глобальные параметры и переменные, во втором — локальные.
Если элементы xsl:param используются для определения локальных переменных, они должны быть первыми дочерними элементами xsl:template, то есть, строго говоря, определение
<xsl:template name="page">
<xsl:text>Text</xsl:text>
<xsl:param name="foo"/>
</xsl:template>
будет некорректным. На самом деле многие процессоры игнорируют эту ошибку, вполне разумно считая, что ничего смертельного в ней нет. Но, конечно, лучше использовать стандартный вариант:
<xsl:template name="page">
<xsl:param name="foo"/>
<xsl:text>Text</xsl:text>
</xsl:template>
Переменные и параметры
Было бы слишком просто и слишком бесполезно лишь констатировать, что переменные и параметры в XSLT похожи между собой и сильно отличаются от переменных в других языках программирования. Мы попытаемся разобраться в этом вопросе несколько глубже — чем именно и ради чего конкретно переменные в XSLT обладают такими свойствами.
В классической книге Монти Бен-Ари "Языки программирования" [Бен-Ари 2000] приводится следующее определение переменной:
Переменная — это имя, присвоенное ячейке памяти или ячейкам, которые могут содержать представление конкретного типа (данных).
Затем следует комментарий:
Значение может изменяться во время выполнения программы.
Если немного сменить уровень абстракции, первая часть этого определения верна и по отношению к параметрам, и переменным XSLT (далее мы будем говорить просто "переменные", поскольку различия между ними на данный момент несущественны). Конечно, где-то там внутри реализации конкретного XSLT-процессора есть память, поделенная на ячейки, в которой процессор хранит информацию. Такие детали нас, естественно, не интересуют, ибо мы больше рассуждаем о логических, чем о физических моделях. В логической же модели переменная представляется как объект определенного типа, с которым связано имя, — по этому имени мы можем обращаться к объекту, использовать его значение и так далее. Иными словами, в XSLT под переменной понимается не более чем ассоциация между значением и именем, и если мы вдруг скажем, что переменная x имеет значение 5, это будет означать, что имя "x" связано объектом численного типа, значение которого равно 5. Заметим небольшую разницу с определением Бен-Ари: мы не говорим о том, что число 5 лежит в какой-то ячейке памяти (хотя, несомненно, оно так и есть) и что этой ячейке присвоено имя "x" — мы говорим об ассоциации между объектом и именем и только.
Теперь, когда более или менее ясно, что же мы имеем в виду под переменными, на новой строчке полужирным шрифтом напишем следующее:
Переменные в XSLT не могут быть изменены.Разработчикам, которые использовали в своей практике только процедурные языки и не имеют опыта функционального или логического программирования будет очень нелегко смириться с такой ситуацией. То, что переменные не могут быть изменены дискредитирует само название — переменные, ибо они уже более похожи на константы.
Практически во всех процедурных языках оператор присваивания вида
переменная = выражение
с незначительными вариациями работает приблизительно следующим образом:
□ сначала вычисляется присваиваемое выражение;
□ затем вычисляется адрес переменной;
□ затем значение, полученное на первом шаге, копируется в ячейки памяти, начиная с адреса, полученного на втором шаге присваивания.
В XSLT нет оператора присваивания. Переменным и параметрам никогда не присваиваются значения в приведенном выше смысле, объявление переменной или параметра лишь связывает указанное имя со значением некоторого выражения. Иными словами, объявление переменной есть создание ассоциации между объектом и именем.
В конце этой главы мы вернемся к неизменяемым переменным и попытаемся объяснить, почему их нельзя изменить — но прежде мы должны научиться их использовать.