Расширение XSLT Печать
Статьи - XSLT

Мы увидели, как сделать XSLT более похожим на программирование. Как насчет добавления к нему фактического программирования? Давайте взглянем на добавление функциональности Java к таблице стилей XSLT.

Во-первых, следует отметить, что хотя механизм расширения является частью рекомендаций XSLT, рассматриваемая здесь реализация специфична для процессора Xalan XSLT. Основные идеи практически одинаковы для других процессоров, однако для уточнения деталей вам придется сверяться с документацией.

Далее мы создадим расширение, которое позволит нам масштабировать рецепт на несколько порций.

Элементы расширения

Расширение XSLT производится с помощью различных методик. Первая - это использование элементов extension. Элемент extension – это элемент в пространстве имен, который указывает на класс Java. Взгляните, например, на следующий элемент extension (см. листинг 30).


Листинг 30. Использование элемента extension

 

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns="http://www.w3.org/TR/xhtml1/strict"

xmlns:scaler = "com.backstop.RecipeScaler"

extension-element-prefixes="scaler">

 

<xsl:template match="/">

<html>

<head>

<title>Recipes</title>

</head>

<body>

 

<scaler:scaleMessage servings="2" />

 

<xsl:apply-templates select="/recipes/recipe"/>

</body>

</html>

</xsl:template>

...

 

Мы создали пространство имен, которое соответствует классу comp.backstop.RecipeScaler, который включает в себя статический метод под названием scaleMessage (см. листинг 31).


Листинг 31. Класс RecipeScaler

 

package com.backstop;

 

public class RecipeScaler {

 

public static String scaleMessage (

org.apache.xalan.extensions.XSLProcessorContext context,

org.w3c.dom.Element thisElement){

 

return "This recipe has been scaled by a factor of " +

thisElement.getAttribute("servings") + ".";

 

}

 

}

 

Добравшись до элемента, процессор видит префикс пространства имен scaler: и знает, что он обозначен как префикс элемента extension, и таким образом понимает, какой класс обозначен в определении пространства имен. Вызываемый им метод отвечает локальному имени элемента - scaleMessage. Сам метод получает два аргумента, из которых мы фактически используем один. Параметр context ссылается на контекст процессора, который позволяет взглянуть на элементы, относящиеся к элементу extension, однако мы просто займемся самим элементом extension. Так как мы получаем этот элемент как параметр метода, мы можем извлечь значения любых атрибутов, добавленных к этому элементу, таких как servings в данном случае. Текст, возвращенный методом, добавляется к выводу на месте элемента extension.

Это означает, что если применить таблицу стилей, мы получим результаты, показанные на рисунке 10.


Рисунок 10. Результаты элемента extension
Результаты элемента extension

Элементы extension могут быть весьма полезны, хотя и несколько сложны в использовании.

Функции extension

Еще один способ добавить функциональность при помощи таблиц стилей - это использование функций extension, которые несколько проще реализовывать, чем элементы extension. Например, мы можем создать функцию, которая умножает количество ингредиентов и количество порций (см. листинг 32).


Листинг 32. Метод scaleIngredient()

 

package com.backstop;

 

public class RecipeScaler {

 

public static String scaleMessage (

org.apache.xalan.extensions.XSLProcessorContext context,

org.w3c.dom.Element thisElement){

 

return "This recipe has been scaled by a factor of " +

thisElement.getAttribute("servings") + ".";

 

}

 

public static int scaleIngredient(int servings, int original){

 

return (servings * original);

 

}

 

}

 

 

Добавление этой функции в таблицу стилей идентично добавлению элемента extension, в котором уже есть карта пространства имен для класса (см.листинг 33).


Листинг 33. Добавление функции extension

 

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns="http://www.w3.org/TR/xhtml1/strict"

xmlns:scaler = "com.backstop.RecipeScaler"

extension-element-prefixes="scaler">

...

<xsl:template match="ingredients/ingredient">

 

<xsl:value-of select="scaler:scaleIngredient(2, ./qty)"/>

<xsl:text> </xsl:text><xsl:value-of select="./unit"/>

<xsl:text> </xsl:text><xsl:value-of select="./food"/><br

/>

 

</xsl:template>

...

 

Учтите, что вызов функции включает префикс пространства имен. Как и ранее, процессор видит префикс и знает, что надо выполнить вызов класса RecipeScaler. В результате количество ингредиентов умножается на два (см. рисунок 11).


Рисунок 11. Использование функции extension
Использование функции extension

Хотя данный код и работает, поддерживать его сложно. Давайте посмотрим, как упростить поддержку.

Программирование XSLT

Прежде, чем закончить изложение, рассмотрим два аспекта XSLT, которые предоставляют некоторые возможности, характерные для обычных языков программирования.