Алексей Валиков - Технология XSLT
<xsl:apply-templates select="@*|node()"/>
режим будет пустым, значит шаблон для xhtml:а вызываться не будет. Для того чтобы применить его при помощи xsl:apply-templates, мы добавим в этот элемент атрибут mode:
<xsl:apply-templates
select=".//xhtml:a[@href and not(xhtml:*)]"
mode="links">
<xsl:sort select="."/>
</xsl:apply-templates>
Разберем более подробно это определение. Данная инструкция будет применять шаблоны с режимом links к узлам, возвращаемым выражением ".//xhtml:a[@href and not (xhtml:*)]", отсортированным в алфавитном порядке своих строковых значений. Выражение ".//xhtml:a[@href and not(xhtml:*)]" возвращает всех потомков текущего узла (путь выборки ".//"), которые принадлежат пространству имен xhtml, являются элементами с именами а, (тест имени "xhtml:a"), при этом имеют атрибут href и не включают в себя другие элементы (предикат "[@href and not (xhtml:*)]").
Преобразование целиком будет иметь следующий вид.
Листинг 5.4. Преобразование, добавляющее перечень ссылок<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:template match="xhtml:body">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<h1>Links found on this page:</h1>
<xsl:apply-templates select=".//xhtml:a[@href and not (xhtml:*)]">
<xsl:sort select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xhtml:a">
<xsl:copy>
<xsl:copy-of select="@href|text()"/>
</xsl:copy>
<br/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Применив это преобразование, например, к главной странице Консорциума W3 (http://www.w3.org), мы получим ее точный дубликат, в конце которого будет приведен перечень всех найденных текстовых ссылок. Выходящий документ будет заканчиваться фрагментом вида:
<h1>Links found on this page:</h1>
<a href="Consortium/">About W3C</a><br/>
<a href="WAI/">Accessibility</a><br/>
<a href="Consortium/Activities">Activities</a><br/>
и так далее.
Заметим, что того же эффекта можно было добиться другими способами, например, при помощи именованных шаблонов или элемента xsl:for-each, однако применение режимов, пожалуй, является наиболее гибкой техникой.
Досадным ограничением режимов является то, что режим нельзя выбирать динамически. Атрибут mode обязан иметь фиксированное значение, то есть вызов вида:
<xsl:apply-templates mode="{$mode}"/>
будет некорректным. Особо серьезных практических противопоказаний для динамических режимов нет, будем надеяться, что в следующих версиях XSLT они появятся.
Именованные шаблоны
Вместо того чтобы при помощи атрибута match указывать, какая часть входящего документа должна преобразовываться данным шаблоном, ему можно присвоить имя и вызывать в любой момент вне зависимости от контекста преобразования. Такие шаблоны очень схожи по принципу с процедурами в императивных языках программирования — они позволяют выносить часто используемые части преобразований, передавать им параметры и вызывать вне зависимости от того, что именно обрабатывается в данный момент.
Имя шаблонному правилу присваивается атрибутом name элемента xsl:template. После этого шаблону более необязательно иметь атрибут match, теперь он может быть вызван просто по имени. Два шаблона с одним порядком импорта не могут иметь одинаковых имен. Если же одинаковые имена имеют шаблоны различного порядка импорта, шаблоны старшего порядка переопределяют младшие шаблоны
ПримерПри генерации HTML-страниц часто встречающейся задачей является создание элемента head. Элемент head, как правило, содержит несколько элементов meta, предоставляющих метаинформацию, элемент title, который определяет название страницы и элемент link, который связывает данную страницу с другими документами, например, с каскадными таблицами стилей (CSS).
Для того чтобы упростить процедуру генерации head, мы можем вынести ее в отдельный именованный шаблон.
Листинг 5.5. Именованный шаблон для генерации элемента head<xsl:template name="head">
<head>
<meta name="keywords" content="XSLT, XPath, XML"/>
<meta name="description"
content="This site is dedicated to XSLT and Xpath."/>
<title>XSLTdev.ru - XSLT developer resource</title>
<link rel="stylesheet" type="text/css" href="style/main.css"/>
</head>
</xsl:template>
Думается, этот шаблон не требует пояснений — он просто создает в входящем документе несколько элементов. Непонятным пока остается другое — как вызывать именованные шаблоны? Элемент xsl:apply-templates явно не подходит, поскольку именованные шаблоны не обязаны иметь атрибут match. Их выполнение производится элементом xsl:call-template.
Элемент xsl:call-template
Приведем синтаксис этого элемента:
<xsl:call-template
name="имя">
<!-- Содержимое: несколько элементов xsl:with-param -->
</xsl:call-template>
Обязательный атрибут name указывает имя шаблона, который вызывается этой инструкцией. Например, шаблон с именем "head", приведенный выше, может быть вызван следующим образом:
<xsl:call-template name="head"/>
Атрибут name при вызове обязан иметь фиксированное значение — точно так же, как и в случае с mode и xsl:apply-templates, динамика здесь не разрешена.
При вызове xsl:call-template не изменяет контекста преобразования. Фактически, вызов именованного шаблона эквивалентен замене в тексте преобразования элемента xsl:call-template на тело вызываемого шаблона.
Приведем пример.
Листинг 5.6. Входящий документ<content>
Just a few words...
</content>
Листинг 5.7. Преобразование<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<xsl:call-template name="head"/>
<body><xsl:copy-of select="content/node()"/></body>
</html>
</xsl:template>
<xsl:template name="head">
<head>
<meta name="keywords" content="XSLT, XPath, XML"/>
<meta name="description"
content="This site is dedicated to XSLT and Xpath."/>
<title>XSLTdev.ru - XSLT developer resource</title>
<link rel="stylesheet" type="text/css" href="style/main.css"/>
</head>
</xsl:template>
</xsl:stylesheet>
Листинг 5.8. Выходящий документ<html>
<head>
<meta name="keywords" content="XSLT, XPath, XML">
<meta name="description"
content="This site is dedicated to XSLT and Xpath.">
<title>XSLTdev.ru - XSLT developer resource</title>
<link rel="stylesheet" type="text/css" href="style/main.css">
</head>
<body>Just a few words...</body>
</html>
ПримечаниеНесколько более эффективным способом использования в документе статических частей (как содержимое элемента head в приведенном примере) является хранение этих частей во внешних документах и вставка их в выходящий документ при помощи элемента xsl:copy-of и функции document.
В этом примере шаблон, обрабатывающий корневой элемент, фактически эквивалентен шаблону вида:
<xsl:template match="/">
<html>
<head>
<meta name="keywords" content="XSLT, XPath, XML"/>
<meta name="description"
content="This site is dedicated to XSLT and Xpath."/>
<title>XSLTdev.ru - XSLT developer resource</title>
<link rel="stylesheet" type="text/css" href="style/main.css"/>
</head>
<body><xsl:value-of select="content"/></body>
</html>
</xsl:template>
В принципе именованные шаблоны не обязаны иметь атрибут match, но он все же может быть определен. В этом случае шаблон можно будет применять как для обработки частей документов элементом xsl:apply-templates, так и вызывая его по имени элементом xsl:call-template.
ПримерИзменим объявление нашего шаблона head следующим образом:
<xsl:template name="head" match="head">
...
</xsl:template>
Теперь, если входящий документ будет иметь вид
<page>