5 Правила шаблона

5.1 Схема обработки

Чтобы в конечном дереве построить фрагмент, обрабатывается перечень исходных узлов. Само конечное дерево строится путем обработки набора, состоящего из одного корневого узла. Перечень исходных узлов обрабатывается посредством помещения в конечное дерево структуры, созданной путем последовательной обработки каждого члена в этом перечне. Обработка узла осуществляется путем нахождения всех правил шаблона, чей образец соответствуют этому узлу, и выбора среди них самого лучшего. Затем обрабатывается шаблон из выбранного правила, причем в качестве текущего узла берется обрабатываемый узел, а как текущий набор узлов используется обрабатываемый перечень исходных узлов. Обычно шаблон содержит инструкции, которые набирают новый набор исходных узлов для дальнейшей обработки. Процесс сравнения, подстановки и нового набора продолжается рекурсивно до тех пор, пока для обработки можно найти новые исходные узлы.

Реализация XMLT может обрабатывать исходный документ любым способом, который дает тот же результат, как если бы выполнялась обработка в соответствии с представленной моделью.

5.2 Образцы

Правило шаблона идентифицирует узлы, к которым оно будет применяться, с помощью образца. Помимо правил шаблона, образцы используются также для нумерации (см. [7.7 Нумерация]) и декларирования ключей (см. [12.2 Ключи]). Образец задает перечень условий для обрабатываемого узла. Узел, который отвечает этим условиям, шаблону соответствует, а узел, который условиям не отвечает, шаблону не соответствует. Синтаксис шаблонов является подмножеством синтаксиса выражений. В частности, в качестве шаблонов могут использоваться пути адресации, которые отвечают определенным ограничениям. Выражение, которое является образцом, обрабатывается всегда как объект типа "набор узлов". Узел соответствует образцу, если он числится в наборе узлов, полученных в результате обработки этого образца как некого выражения в неком возможном контексте. Возможные контексты - это такие контексты, чей узлом контекста является проверяемый узел или один из его предков.

Некоторые примеры шаблонов:

Образец должен отвечать грамматике Pattern. Pattern представляет собой набор образцов для путей адресации, разделенных символом |. Образец для пути адресации - это некий путь адресации, на всех шагах которого используются лишь оси child или attribute. Хотя образцы и не должны использовать ось descendant-or-self, в образцах могут использоваться как оператор //, так и оператор /. Образцы путей адресации могут начинаться также с вызова функций id или key с фиксированным аргументом. В предикатах образца могут использоваться произвольные выражения так это происходит в предикатах пути адресации.

Образцы
[1]    Pattern    ::=    LocationPathPattern
| Pattern '|' LocationPathPattern
[2]    LocationPathPattern    ::=    '/' RelativePathPattern?
| IdKeyPattern (('/' | '//') RelativePathPattern)?
| '//'? RelativePathPattern
[3]    IdKeyPattern    ::=    'id' '(' Literal ')'
| 'key' '(' Literal ',' Literal ')'
[4]    RelativePathPattern    ::=    StepPattern
| RelativePathPattern '/' StepPattern
| RelativePathPattern '//' StepPattern
[5]    StepPattern    ::=    ChildOrAttributeAxisSpecifier NodeTest Predicate*
[6]    ChildOrAttributeAxisSpecifier    ::=    AbbreviatedAxisSpecifier
| ('child' | 'attribute') '::'

Считается что узел соответствует образцу тогда и только тогда, когда есть такой возможный контекст, что если с этим контекстом данный образец обрабатывать как выражение, то этот узел будет среди полученного набора узлов. Когда узел соответствует образцу, то для возможных контекстов используется узел контекста, которому соответствует либо сам этот элемент, либо какой-либо предок этого узла, а контекстный набор узлов содержит только этот узел контекста.

Например, образец p соответствует любому элементу p, потому что если взять любой элемент p и обработать выражение p, используя в качестве контекста родителя элемента p, то одним из членов полученного набора узлов будет этот элемент p.

Замечание: Такой образец соответствует даже элементу p, являющемуся элементом этого документа, поскольку корень документа является родителем элемента документа.

Хотя семантика образцов формулируется косвенно в терминах обработки выражений, смысл образца легко понять сразу, не прибегая к терминологии обработки выражений. Символ | в образце обозначает альтернативу. Соответствие образцу с одним или несколькими символами |, разделяющими альтернативы, засчитывается если есть соответствие одной из этих альтернатив. Для образца, состоящего из последовательности StepPattern-ов, разделенных / или //, соответствие проверяется справа налево. Соответствие образцу засчитывается только если есть соответствие самому правому StepPattern, а соответствующий элемент отвечает остальной части образца. Если разделителем является /, то таким элементом может быть только родитель, если же разделителем является //, то в качестве такого элемента может использоваться любой из предков. Соответствие StepPattern, использующему ось child, засчитывается если для данного узла NodeTest дает true, а сам узел не является узлом атрибута. Соответствие StepPattern, использующему ось attribute, засчитывается если для данного узла NodeTest дает true, а сам узел является узлом атрибута. Если присутствует [], то в StepPattern сперва обрабатывается PredicateExpr, причем проверяемый узел используется как узел контекста, а в качестве контекстного набора узлов берутся узлы, которые имеют общего родителя с узлом контекста и соответствуют NodeTest. Однако если проверяемый узел является узлом атрибута, то в этом случае контекстный набор узлов - это все атрибуты, имеющие с проверяемым атрибутом общего родителя и соответствующие NameTest.

Например,

appendix//ulist/item[position()=1]

соответствует узлу тогда и только тогда, когда выполняются все следующие условия:

5.3 Определение правил шаблона

<!-- Category: top-level-element -->
<xsl:template
  match = pattern
  name = qname
  priority = number
  mode = qname>
  <!-- Content: (xsl:param*, template) -->
</xsl:template>

Правило шаблона задается элементом xsl:template. Атрибут match соответствует образу, который идентифицирует исходные узел или узлы, к которым это правило применяется. Если элемент xsl:template не имеет атрибута name, атрибут match обязателен (см. [6 Именованные шаблоны]). Если в значении атрибута match содержится VariableReference, фиксируется ошибка. Содержимое элемента xsl:template является шаблоном, который обрабатывается если данное правило шаблона задействовано.

Например, документ XML может содержать:

This is an <emph>important</emph> point.

Приведенное далее правило шаблона соответствует элементам emph и создает объект форматирования fo:inline-sequence, имеющий свойство font-weight со значением bold.

<xsl:template match="emph">
  <fo:inline-sequence font-weight="bold">
    <xsl:apply-templates/>
  </fo:inline-sequence>
</xsl:template>
Примечание: В примерах этого документа префикс fo: используется для пространства имен http://www.w3.org/1999/XSL/Format (пространства имен объектов форматирования, определенных в [XSL]).

Как описано далее, элемент xsl:apply-templates рекурсивно обрабатывает непосредственные потомки данного исходного элемента.

5.4 Использование правил шаблона

<!-- Category: instruction -->
<xsl:apply-templates
  select = node-set-expression
  mode = qname>
  <!-- Content: (xsl:sort | xsl:with-param)* -->
</xsl:apply-templates>

В данном примере для элемента chapter создается блок, а затем обрабатывается его непосредственный потомок.

<xsl:template match="chapter">
  <fo:block>
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

В отсутствие атрибута select инструкция xsl:apply-templates обрабатывает все непосредственные потомки текущего узла, включая узлы текста. Однако те текстовые узлы, которые были вычищены как описано в главе [3.4 Удаление пробельных символов], обрабатываться не будут. Если очистка узлов с пробельными символами для элемента не была разрешена, то все пробельные символы в содержимом этого элемента будут обрабатываться как текст, и, следовательно, пробельный символ между элементами - непосредственными потомками будет учитываться при вычислении положения элемента - непосредственного потомка, возвращаемого функцией position.

Чтобы обрабатывать не все непосредственные потомки, а лишь узлы, отобранные по некому выражению, может использоваться атрибут select. Значением атрибута select является выражение. После обработки этого выражения должен получиться набор узлов. Если нет указаний по сортировке (см. [10 Сортировка]), собранный перечень узлов обрабатывается в том порядке, как они следуют в документе. В следующем примере обрабатываются все непосредственные потомки author для этого элемента author-group:

<xsl:template match="author-group">
  <fo:inline-sequence>
    <xsl:apply-templates select="author"/>
  </fo:inline-sequence>
</xsl:template>

В следующем примере обрабатываются все содержащиеся в author элементы given-name, которые являются непосредственным потомком author-group:

<xsl:template match="author-group">
  <fo:inline-sequence>
    <xsl:apply-templates select="author/given-name"/>
  </fo:inline-sequence>
</xsl:template>

В данном примере обрабатываются все элементы heading, являющиеся потомками элемента book.

<xsl:template match="book">
  <fo:block>
    <xsl:apply-templates select=".//heading"/>
  </fo:block>
</xsl:template>

Есть также возможность обрабатывать элементы, которые не являются потомками текущего узла. В данном примере предполагается, что элемент department имеет непосредственный потомок group и потомки employee. Сперва находится отдел employee, а затем обрабатывается непосредственный потомок group элемента department.

<xsl:template match="employee">
  <fo:block>
    Employee <xsl:apply-templates select="name"/> belongs to group
    <xsl:apply-templates select="ancestor::department/group"/>
  </fo:block>
</xsl:template>

Для выполнения простого переупорядочения в пределах одного шаблона можно использовать сразу несколько элементов xsl:apply-templates. В следующем примере создаются две таблицы HTML. Первая таблица заполняется отечественными продажами, вторая - международными.

<xsl:template match="product">
  <table>
    <xsl:apply-templates select="sales/domestic"/>
  </table>
  <table>
    <xsl:apply-templates select="sales/foreign"/>
  </table>
</xsl:template>
Замечание: Может оказаться, что шаблону соответствуют два потомка, причем один из них является потомком другого. Как-то особенно такой случай не обрабатывается: оба потомка будут обработаны обычным образом. Например, дан исходный документ
<doc><div><div></div></div></doc>
правило
<xsl:template match="doc">
  <xsl:apply-templates select=".//div"/>
</xsl:template>
будет обрабатывать и внешний, и внутренний элементы div.
Замечание: Обычно xsl:apply-templates используется для обработки только тех узлов, которые являются потомками текущего узла. Такое использование xsl:apply-templates не может привести к появлению бесконечных циклов обработки. Однако когда xsl:apply-templates используется для обработки элементов, не являющихся потомками текущего узла, появляется вероятность возникновения бесконечных циклов. Например,
<xsl:template match="foo">
  <xsl:apply-templates select="."/>
</xsl:template>
Реализации процессора в некоторых случаях могут выявить такие циклы, однако остается вероятность, что стиль попадет в такой бесконечный цикл, который эта реализация не сможет обнаружить. Для безопасности это может представлять угрозу типа "отказ в обслуживании".

5.5 Разрешение конфликтов в правилах шаблона

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

  1. Во-первых, из рассмотрения исключаются все соответствующие узлу правила шаблона, которые имеют более низкий приоритет импорта, чем проверяемое правило шаблона и правила с наивысшим приоритетом импорта.

  2. Затем из рассмотрения исключаются все соответствующие узлу правила шаблона, которые имеют более низкий приоритет чем данное соответствующее правило шаблона и правила с наивысшим приоритетом. Приоритет правила шаблона задается атрибутом priority. Значением этого атрибута должно быть реальное число (положительное или отрицательное), соответствующее сценарию Number с необязательным начальным символом минус (-). Приоритет по умолчанию вычисляется следующим образом:

    Таким образом, самый общий тип образцов (образец, проверяющий узел определенного типа и с определенным расширенным именем) имеет приоритет 0. Следующий, более общий тип образца (образец, проверяющий узел определенного типа и с расширенным именем и определенным URI пространства имен) имеет приоритет -0.25. Еще более общие образцы (образцы, проверяющие лишь узлы определенных типов) имеют приоритет -0.5. Образцы, еще более специальные, чем самый общий тип образца, имеют приоритет 0.5.

Если и после этого останется более одного правила шаблона, соответствующего узлу, фиксируется ошибка. XSLT процессор может сигнализировать о такой ошибке, если же он этого не делает, то должен ее обработать, выбрав среди оставшихся соответствующих узлу правил шаблона то, которое в стиле было указано последним.

5.6 Переопределение правил шаблона

<!-- Category: instruction -->
<xsl:apply-imports />

Правило шаблона, которое должно использоваться для переопределения другого правила шаблона в импортированном стиле (см. [5.5 Разрешение конфликтов в правилах шаблона]), может воспользоваться элементом xsl:apply-imports чтобы вызвать переопределенное правило шаблона.

При обработке стиля в каждой точке имеется некое текущее правило шаблона. Всякий раз, когда по образцу выбирается правило шаблона, для обработки оно становится текущим правилом шаблона. Когда обрабатывается элемент xsl:for-each, то при обработке содержимого этого элемента xsl:for-each текущее правило шаблона становится нулевым.

xsl:apply-imports обрабатывает текущий узел используя лишь те правила шаблона, которые были импортированы в тот элемент стиля, где это текущее правило шаблона находится. Узел обрабатывается в режиме текущего правила шаблона. Если xsl:apply-imports обрабатывается когда текущее правило шаблона нулевое, фиксируется ошибка.

Например, предположим что стиль doc.xsl содержит правило шаблона для элементов example:

<xsl:template match="example">
  <pre><xsl:apply-templates/></pre>
</xsl:template>

Другой стиль может импортировать doc.xsl и поменять обработку элементов example следующим образом:

<xsl:import href="doc.xsl"/>

<xsl:template match="example">
  <div style="border: solid red">
     <xsl:apply-imports/>
  </div>
</xsl:template>

В результате суммарного действия example должен преобразовываться в элемент следующего вида:

<div style="border: solid red"><pre>...</pre></div>

5.7 Режимы

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

Оба элемента xsl:template и xsl:apply-templates имеют необязательный атрибут mode. Значением атрибута mode является название QName, которое приводится к расширенному имени как было описано в главе [2.4 Полные имена]. Если xsl:template не имеет атрибута match, то он не должен иметь и атрибута mode. Если элемент xsl:apply-templates имеет атрибут mode, то он применяется только к тем правилам шаблона из элементов xsl:template, которые имеют атрибут mode с тем же значением. Если элемент xsl:apply-templates атрибута mode не имеет, то он применяется только к тем правилам шаблона из элементов xsl:template, которые также не имеют атрибута mode.

5.8 Встроенные правила шаблона

Имеется встроенное правило шаблона, позволяющее рекурсивно продолжать обработку в отсутствии успешного сравнения с явным правилом шаблона в стиле. Это правило шаблона используется как для узлов элементов, так и для корневого узла. Далее приведен эквивалент встроенного правила шаблона:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

Для каждого метода также есть встроенное правило шаблона, позволяющее рекурсивно продолжать обработку в том же самом режиме, в отсутствии успешного сравнения с образцом из явного правила шаблона в этом стиле. Это правило шаблона применяется как для узлов элементов, так и для корневого узла. Далее приведен эквивалент встроенного правила шаблона для режима m.

<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

Также есть встроенное правило шаблона для текстовых узлов и узлов атрибутов, которое просто копирует текст:

<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

Для инструкций обработки и комментариев встроенное правило шаблона не должно делать ничего.

<xsl:template match="processing-instruction()|comment()"/>

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

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

Назад | Содержание | Вперед