Главная » XSLT » Реализация функций расширения на языке Java

0

Задача

Требуется написать собственное расширение на языке Java.

Решение

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

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

package com.ora.xsltckbk.util;

public class HexConverter {

public static String toHex(String intString)

{

try {

Integer temp = new Integer(intString) ;

return new String("0x").concat(Integer.toHexString(temp.intValue( ))) ;

}

catch (Exception e) {

return new String("0x0") ;

}

}

}

Исходя из того, что значение возвращается с префиксом 0x, можно сделать вывод, что эта функция, скорее всего, будет использоваться в приложении для ге­нерации кода, например так:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt" xmlns:hex="xalan://com.ora.xsltckbk.util.HexConverter" exclude-result-prefixes="hex xalan">

<xsl:template match="group">

enum <xsl:value-of select="@name"/>

{

<xsl:apply-templates mode="enum"/> } ;

</xsl:template>

<xsl:template match="constant" mode="enum"> <xsl:variable name="rep">

<xsl:call-template name="getRep"/> </xsl:variable>

<xsl:value-of select="@name"/> = <xsl:value-of select="$rep"/> <xsl:if test="following-sibling::constant">

<xsl:text>,</xsl:text> </xsl:if> </xsl:template>

<xsl:template match="constant"> <xsl:variable name="rep">

<xsl:call-template name="getRep"/> </xsl:variable>

const int <xsl:value-of select="@name"/> = <xsl:value-of select="$rep"/> ; </xsl:template>

<xsl:template name="getRep"> <xsl:choose>

<xsl:when test="@rep = ‘hex’">

<xsl:value-of select="hex:toHex(@value)"/> </xsl:when> <xsl:otherwise>

<xsl:value-of select="@value"/> </xsl:otherwise> </xsl:choose> </xsl:template>

</xsl:stylesheet>

В следующем примере показано, как можно конструировать Java-объекты и вызывать их методы. При преобразовании XML в SVG возникают трудности с размещением текста. В SVG невозможно узнать, сколько места будет занимать выведенная строка. К счастью, такой способ предоставляет Java. Вопрос в том, насколько правильно «мнение» Java о размере строки, нарисованной конкретным шрифтом, с точки зрения SVG. Но все равно идея кажется заманчивой и ее стоит проверить:

package com.ora.xsltckbk.util ; import java.awt.* ; import java.awt.geom.* ; import java.awt.font.* ; import java.awt.image.*;

public class SVGFontMetrics {

public SVGFontMetrics(String fontName, int size)

{

m_font = new Font(fontName, Font.PLAIN, size) ; BufferedImage bi

= new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB) , m_graphics2D = bi.createGraphics( ) ;

}

public SVGFontMetrics(String fontName, int size, boolean bold,

boolean italic)

{

m_font = new Font(fontName, style(bold,italic) , size) ; BufferedImage bi

= new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); m_graphics2D = bi.createGraphics( ) ;

}

public double stringWidth(String str)

{

FontRenderContext frc = m_graphics2D.getFontRenderContext( ); TextLayout layout = new TextLayout(str, m_font, frc); Rectangle2D rect = layout.getBounds( ) ; return rect.getWidth( ) ;

}

public double stringHeight(String str)

{

FontRenderContext frc = m_graphics2D.getFontRenderContext( ); TextLayout layout = new TextLayout(str, m_font, frc); Rectangle2D rect = layout.getBounds( ) ; return rect.getHeight( ) ;

}

static private int style(boolean bold, boolean italic) {

int style = Font.PLAIN ; if (bold) { style |= Font.BOLD;} if (italic) { style |= Font.ITALIC;} return style ;

}

private Font m_font = null ;

private Graphics2D m_graphics2D = null;

Здесь для получения нужной информации используются классы Graphics2D и TextLayout из Java 2 (JDK 1.3.1). Мы реализовали два открытых конструктора: один – для нормального шрифта, другой – для полужирного или курсивного. Два открытых метода – stringWidth() и stringHeight() – возвращают информа­цию о том, сколько места будет занимать строка, выведенная заданным в конструк­торе шрифтом. Для большинства употребительных шрифтов результат получается довольно точным, но гарантий никаких нет, надо экспериментировать. Для тестирования мы написали такую таблицу стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt"

xmlns:font="xalan://com.ora.xsltckbk.util.SVGFontMetrics" exclude-result-prefixes="font xalan">

<xsl:output method="xml"/>

<xsl:template match="/">

<svg width="10 0%" height="10 0%">

<xsl:apply-templates/> </svg> </xsl:template>

<xsl:template match="text">

<xsl:variable name="fontMetrics"

select="font:new(@font, @size, boolean(@weight), boolean(@stytle))"/>

<xsl:variable name="text" select="."/>

<xsl:variable name="width" select="font:stringWidth($fontMetrics, $text)"/> <xsl:variable name="height" select="font:stringHeight($fontMetrics, $text)"/>

<xsl:variable name="style"> <xsl:if test="@style">

<xsl:value-of select="concat(‘font-style:’,@style)"/> </xsl:if> </xsl:variable> <xsl:variable name="weight"> <xsl:if test="@weight">

<xsl:value-of select="concat(‘font-weight:’,@weight)"/> </xsl:if> </xsl:variable>

<g style="font-family:{@font};font-size:{@size};{$style};{$weight}"> <!– Вызываем SVGFontMetrics, чтобы нapисoвaть пpямoyгoльник, –> <!— paзмеpы ra^poro чуть больше ожидаемых paзмеpoв текста. —> <!– Koppектиpyем y с учетом paзмеpa пpедшествyющегo текста. –> <rect x="10"

y="{sum(preceding-sibling::text/@size) * 2}pt"

width="{$width + 2}" height="{$height + 2}"

style="fill:none;stroke: black;stroke-width:0.5;"/> <!– Выводит текст, центрируя его в прямоугольнике –> <text x="11"

y="{sum(preceding-sibling::text/@size) * 2 + @size div 2 + 2}pt"> <xsl:value-of select="."/>

</text> </g>

</xsl:template> </xsl:stylesheet>

Как показано на рис. 14.1, этот тест дает вполне приличные результаты для некоторых часто используемых шрифтов.

Рис. 14.1. Создание охватывающих прямоугольников правильного размера с помощью расширения SVGFontMetrics

<TextWidthTest>

<text font="Serif" size="9">M’s are BIG; l’s are small;</text> <text font="Serif" size="10">SVG makes handling text no fun at all</text> <text font="Helvetica" size="12">But if I cheat with a little Java</text> <text font="Arial" size="14" weight="bold">PROMISE ME YOU WON’T TELL MY MAMMA! </text>

<text font="Century" size="16" style="italic">But if you do, I won’t lose cheer. </text>

<text font="Courier New" size="18" weight="bold" style="italic">Its

really my tech editor that I fear!</text>

</TextWidthTest>

Обсуждение

Примеры из раздела «Решение» работают без изменения и в Xalan, и в Saxon (несмотря на пространство имен xml.apache.org/xslt). Объясняется это тем, что мы воспользовались сокращенным синтаксисом объявления Java-класса в пространстве имен.

Отметим, что доступ к конструкторам производится с помощью функции new(), и процессоры XSLT понимают, какой из перегруженных конструкторов вызывать, анализируя аргументы. Методам экземпляра Java-класса передается дополнительный (первый по счету) параметр, соответствующий this. На приме­ре HexConverter видно, что при вызове статических методов параметр this не передается.

Пример SVGFontMetrics не работает с более старыми версиями JDK, но ана­логичные результаты можно получить, используя класс java.awt.FontMetrics в сочетании с классом java.awt.Graphics:

package com.ora.xsltckbk.util ;

import java.awt.* ;

import java.awt.geom.* ;

import java.lang.System ;

public class FontMetrics

{

public FontMetrics(String fontName, int size)

{

// Подойдет любой конкретный класс Label component = new Label( ) ; m_metrics

= component.getFontMetrics(

new Font(fontName, Font.PLAIN, size)) ; m_graphics = component.getGraphics( ) ;

}

public FontMetrics(String fontName, int size, boolean bold, boolean italic)

{

// Подойдет любой конкретный класс Label component = new Label( ) ; m_metrics

= component.getFontMetrics(

new Font(fontName, style(bold,italic) , size)) ; m_graphics = component.getGraphics( ) ;

}

// Простой, но менее точный для некоторых шрифтов метод

public int stringWidth(String str) {

return m_metrics.stringWidth(str) ;

}

// Для большинства шрифтов оказывается точнее

public double stringWidthImproved(String str) {

Rectangle2D rect = m_metrics.getStringBounds(str, m_graphics) ; return rect.getWidth( ) ;

}

static private int style(boolean bold, boolean italic) {

int style = Font.PLAIN ; if (bold) { style |= Font.BOLD;} if (italic) { style |= Font.ITALIC;} return style ;

}

private java.awt.FontMetrics m_metrics = null; private java.awt.Graphics m_graphics = null ;

}

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

1.              Использовать вместо xsl:key имеющийся в библиотеке Java класс Hashtable. Он обеспечивает более точный контроль над тем, какие эле­менты индексируются, и позволяет изменять индекс во время выполнения. В определении xsl:key нельзя ссылаться на переменные, а этот способ не подвержен такому ограничению. Им можно воспользоваться для построе­ния индекса над несколькими документами.

2.              Реализовать функцию сортировки узлов, которая компенсирует ограниче­ния xsl:sort, например, позволяет сортировать, применяя правила иностранного языка. Дуг Тидвелл (Doug Tidwell) демонстрирует такую технику для процессора Saxon в книге XSLT (O’Reilly, 2001).

3.              Читать и выводить документы в разных форматах в одной таблице стилей. Например, таким образом можно, помимо XML, читать текстовые файлы, файлы в формате CSV или в двоичном формате. В этом направлении XSLT 2.0 предоставляет элемент xsl:result-document.

4. Извлекать сжатый XML-документ непосредственно из zip-файла с помо­щью класса java.util.zip.ZipFile. Полезно было бы изучить исход­ный код функции document имеющегося у вас процессора XSLT.

См. также

В главе 9 мы отложили решение задачи размещения текста в узлах сгенери­рованного SVG-дерева. Можно было бы применить для этой цели функцию

SVGFontMetrics.

Хотя это и не имеет непосредственного отношения к расширениям XSLT, раз­работчикам, интересующимся Java и SVG, было бы полезно познакомиться с сис­темой Batik (http://xml.apache.org/batik/index.html).

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии