Технология XSLT — страница 52 из 66

Другой, несколько реже используемой, но не менее мощной возможностью расширения XSLT являются элементы расширения. В отличие от обычных элементов, при выполнении преобразования элементы расширения не просто копируются в выходящее дерево. При их обработке процессор должен выполнить определенные действия. Например, многие XSLT-процессоры, написанные на Java, позволяют связывать элементы расширения с методами Java-классов.

Пример

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

This page was generated at 10:23.

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

This page was generated at .

элемент расширения

ext:time
должен быть заменен текущим временем. Ниже мы приведем пример реализации этого элемента для процессора Xalan.

Интерфейс программирования расширений в Xalan требует, чтобы для каждого элемента расширения был определен метод вида:

тип элемент(org.apache.xalan.extensions.XSLProcessorContext context,

            org.apache.xalan.templates.ElemExtensionCall elem)

где

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

public String time(XSLProcessorContext context,

                   ElemExtensionCall elem)

Два аргумента, которые передаются методу элемента расширения, описывают контекст преобразования (

XSLProcessorContext
) и параметры вызова элемента расширения (
ElemExtensionCall
). Чуть позже мы покажем, как можно использовать эти объекты для создания более функциональных элементов расширения; пока же продолжим с элементом
ext:time
.

Следующим шагом мы создадим класс расширения

ext.java
, в котором реализуем описанный выше метод
time
.

Листинг 10.18 Класс ext.java

package de.fzi.xslt;


import java.util.Date;

import java.text.SimpleDateFormat;

import org.apache.xalan.extensions.XSLProcessorContext;

import org.apache.xalan.templates.ElemExtensionCall;


public class ext {


 public String time(XSLProcessorContext context,

  ElemExtensionCall elem) {

  SimpleDateFormat df = new SimpleDateFormat("HH:mm");

  return df.format(new Date());

 }

}

Равно как и в случае с функциями расширения, связующим звеном между элементами и Java-имплементацией их семантики служат пространства имен. В нашем случае класс

de.fzi.xslt.ext
может быть связан с префиксом пространства имен
ext
следующим объявлением:

xmlns:ext="xalan://de.fzi.xslt.ext"

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

extension-element-prefixes
элемента
xsl:stylesheet
:

 ...

 extension-element-prefixes="ext">

 ...

В итоге наше преобразование будет иметь следующий вид.

Листинг 10.19. Преобразование, использующее элемент расширения

 version="1.0"

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

 xmlns:ext="xalan://de.fzi.xslt.ext"

 extension-element-prefixes="ext">


This page was generated at .


Результатом этого преобразования будет документ вида:

This page was generated at 11:56.

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

ext:date
, который будет выводить текущую дату или время в формате, зависящем от значения его атрибута
pattern
.

Листинг 10.20. Класс ext.java реализация элемента ext:date

package de.fzi.xslt;


import java.util.Date;

import java.text.SimpleDateFormat;

import org.apache.xalan.extensions.XSLProcessorContext;

import org.apache.xalan.templates.ElemExtensionCall;


public class ext{

 public String date(XSLProcessorContext context, ElemExtensionCall elem) {

  SimpleDateFormat df;


  // Получаем значение атрибута pattern элемента расширения

  String pattern = elem.getAttribute("pattern");

  // Если атрибут pattern не определен,

  // используем образец форматирования, определенный по умолчанию

  if (pattern == null)

   df = new SimpleDateFormat();

  // Если атрибут pattern определен, используем его значение

  // в качестве образца форматирования

  else

   df = new SimpleDateFormat(pattern);

  return df.format(new Date());

 }

}

В преобразовании этот элемент мы можем использовать как:

This page was generated at on

.

или:

This page was generated on .

В первом случае результатом будет:

This page was generated at 12:11 on 08/10/2001.

Во втором:

This page was generated on 08.10.01 12:11.

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

Функция element-available

boolean element-available(string)

Функция

element-available
совершенно аналогична функции
function-available
: она служит для проверки доступности в преобразовании того или иного элемента. Строковый параметр
element-available
задает расширенное имя элемента; функция возвращает
true
, если элемент с таким именем доступен,
false
— если нет.

Пример

Предположим, что преобразование, созданное нами для процессора Xalan с использованием элемента расширения

ext:date
, будет выполняться на каком-либо другом процессоре. В этом случае велика вероятность того, что вследствие несовместимости механизмов расширений это преобразование завершится ошибкой — "чужой" процессор просто не сможет выполнить элемент
ext:date.

Во избежание этого, мы можем использовать функцию

element-available
для проверки доступности элемента
ext:date
до его вызова.

Листинг 10.21. Преобразование, использующее функцию element-available

 version="1.0"

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

 xmlns:ext="xalan://de.fzi.xslt.ext"

 extension-element-prefixes="ext">


This page was generated at on .

Элемент xsl:fallback

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

xsl:fallback
. Синтаксическая конструкция этого элемента следующая:

Элемент

xsl:fallback
включается в "критическую" инструкцию, то есть в элемент, который может быть неизвестен процессору. В случае, если критическая инструкция отрабатывается нормально, содержимое
xsl:fallback
попросту игнорируется. Иначе, если процессор в силу некоторых причин не может выполнить критическую инструкцию, вместо нее он будет выполнять содержимое дочернего элемента
xsl:fallback
.

Пример

На тот случай, если процессор не сможет выполнить наш элемент расширения

ext:date
, мы можем "подстраховать" его следующим образом:

unknown time

В этом случае шаблон

This page was generated at

unknown time

.

в случае невозможности выполнить

ext:date
выведет

This page was generated at unknown time.

Заметим, что

xsl:fallback
применим не только для обработки исключительных ситуаций, связанных с элементами расширения. Наборы доступных процессору элементов XSLT будут также меняться от версии к версии, и
xsl:fallback
вполне пригодится для обеспечения обратной совместимости. Например, если в версии XSLT 2.0 будет определен элемент
xsl:for-each-group
, то
xsl:fallback
можно использовать при создании альтернативного варианта для процессоров, которые еще не поддерживают новую версию:

Инициатива EXSLT