<xsclass="underline" otherwise>
<source name="{@source}">
<xsclass="underline" copy-of select="self::node()
|following-sibling::item[@source=current()/@source]"/>
</source>
</xsclass="underline" otherwise>
</xsclass="underline" choose>
</xsclass="underline" template>
</xsclass="underline" stylesheet>
Бесспорно, решение было несложным, но довольно громоздким. Самым же узким местом в этом преобразовании является обращение к элементам item источника текущего элемента посредством сравнения атрибутов source.
Проблема совершенно стандартна для многих преобразований: нужно выбирать узлы по определенным признакам, причем делать это нужно как можно более эффективно. Хорошо, что в нашем документе было всего восемь элементов item, но представьте себе ситуацию, когда элементов действительно много.
Проблема, которую мы подняли, достаточно серьезна. Она состоит в оптимизации поиска узлов с определенными свойствами в древовидно организованной структуре.
Попробуем разобраться в смысле фразы "узел обладает определенными свойствами". Очевидно, это означает, что для этого узла выполняется некое логическое условие, иначе говоря, некий предикат обращается в "истину".
Однако какого именно типа условия мы чаще всего проверяем? Анализируя различные классы задач, можно придти к выводу, что в большинстве случаев предикаты являются равенствами — выражениями, которые обращаются в "истину" тогда и только тогда, когда некоторый параметр узла, не зависящий от текущего контекста, равен определенному значению. В нашем примере смысл предиката на самом деле состоит не в том, чтобы проверить на истинность выражение @source=current()/@source, а в том, чтобы проверить на равенство @source и current()/@source.
Если переформулировать это для общего случая, то нам нужно выбрать не те узлы, для которых истинно выражение A=B, скорее нужно выбрать те, для которых значение A равно значению B. Иначе говоря, узел будет идентифицироваться значением в своего свойства A. И если мы заранее вычислим значения свойств A, проблема поиска узлов в дереве сведется к классической проблеме поиска элементов множества (в нашем случае — узлов дерева) по определенным значениям ключей (в нашем случае — значениями свойств A).
Чтобы пояснить это, вернемся к нашему примеру: мы ищем элементы item со значением атрибута source, равным заданному. Свойством, идентифицирующим эти элементы, в данном случае будут значения их атрибутов source, которые мы можем заранее вычислить и включить в табл. 8.2.
Таблица 8.2. Значения атрибута source элементов item
Идентификатор (значение атрибута source) |
Элемент item |
|---|---|
a |
<item source="a" name="A"/> |
a |
<item source="a" name="C"/> |
a |
<item source="a" name="H"/> |
b |
<item source="b" name="B"/> |
b |
<item source="b" name="E"/> |
b |
<item source="b" name="F"/> |
с |
<item source="c" name="D"/> |
с |
<item source="c" name="G"/> |
Таким образом, значение "с" идентифицирует объекты с именами D и G, а значение "а" — объекты с именами A, C и H, причем находить соответствующие элементы в таблице по их ключевому свойству не составляет никакого труда.
Несмотря на то, что произведенные нами манипуляции чрезвычайно просты (и настолько же эффективны), процессор вряд ли в общем случае сможет сделать что-либо подобное сам, и потому очень важной является возможность явным образом выделять в XSLT-преобразованиях ключевые свойства множеств узлов.
В этом разделе мы будем рассматривать две конструкции, позволяющие манипулировать множествами узлов посредством ключей — это элемент xsclass="underline" key, который определяет в преобразовании именованный ключ, и функция key, которая возвращает множество узлов, идентифицирующихся заданными значениями ключей.
Элемент xsclass="underline" key
Синтаксис элемента несложен:
<xsclass="underline" key
name="имя"
match="паттерн"
use="выражение"/>
Элемент верхнего уровня xsclass="underline" key определяет в преобразовании ключ именем, заданным в значении атрибута name, значением которого для каждого узла документа, соответствующего паттерну match, будет результат вычисления выражения, заданного в атрибуте use. Ни атрибут use, ни атрибут match не могут содержать переменных.
В нашем примере элементы item идентифицируются значениями своих атрибутов source. Для их идентификации мы можем определить ключ с именем src следующим образом:
<xsclass="underline" key name="src" match="item" use="@source"/>
Следуя строгому определению, данному в спецификации языка, ключом называется тройка вида (node, name, value), где node — узел, name — имя и value — строковое значение ключа. Тогда элементы xsclass="underline" key, включенные в преобразование, определяют множество всевозможных ключей обрабатываемого документа. Если этому множеству принадлежит ключ, состоящий из узла x, имени у и значения z, говорят, что узел x имеет ключ с именем у и значением z или что ключ у узла x равен z.
Ключ src из предыдущего примера определяет множество, которое состоит из следующих троек:
(<item name="A".../>, 'src', 'a')
(<item name="B".../>, 'src', 'b')
(<item name="C".../>, 'src', 'a')
(<item name="D".../>, 'src', 'c')
...
(<item name="H".../>, 'src', 'a')
В соответствии с нашими определениями мы можем сказать, что элемент
<item source="b" name="B"/>
имеет ключ с именем "src" и значением "b" или что ключ "src" элемента