XSLT Extensions
 

In this page, I am compiling some examples for XSLT extensions. Extensions allows us to extend the XSLT language.
 

1. Image handling using XSLT

The following question was asked on XSL-List.

> Has anyone written an XSLT 1.0 extension function to get the
> height and width of an image at a specified URL?

Following is an example of Java extension function (tested with JDK 1.6.0) for Xalan-J:

ImageHelper.java
------------------

import java.net.URL;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;

public class ImageHelper {

    public static int getHeight(String imgUrl) {
       
        int height = -1;

        try {
            URL url = new URL(imgUrl);
            BufferedImage buffImg = ImageIO.read(url);
            height = buffImg.getHeight();
        }
        catch(java.net.MalformedURLException ex) {
            ex.printStackTrace();
        }
        catch(java.io.IOException ex) {
            ex.printStackTrace();
        }

        return height;
    }

    public static int getWidth(String imgUrl) {

        int width = -1;

        try {
            URL url = new URL(imgUrl);
            BufferedImage buffImg = ImageIO.read(url);
            width = buffImg.getWidth();
        }
        catch(java.net.MalformedURLException ex) {
            ex.printStackTrace();
        }
        catch(java.io.IOException ex) {
            ex.printStackTrace();
        }

        return width;
    }

}


Following is the test stylesheet (named, test.xsl):

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

<xsl:output method="text" />

<xsl:template match="/">
    <xsl:variable name="imageUrl" select="'http://www.google.co.in/intl/en/images/about_logo.gif'" />
    <xsl:variable name="imgHeight" select="java:ImageHelper.getHeight($imageUrl)" />
    <xsl:variable name="imgWidth" select="java:ImageHelper.getWidth($imageUrl)" />

    Height - <xsl:value-of select="$imgHeight" /><xsl:text>&#xa;</xsl:text>
    Width - <xsl:value-of select="$imgWidth" />
</xsl:template>

</xsl:stylesheet>

When Xalan-J is run as following:
java org.apache.xalan.xslt.Process -in test.xsl -xsl test.xsl

The output produced is:
Height - 65
Width - 175


2.
Grouping and numbering

The following question was asked on XSL-List.

I need to run through a large structure that resembles:

<sample>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>300</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
</sample>

Firstly, I need to sort the data into <group_id> order, so I use:
<xsl:apply-templates select="sample//result/details">
    <xsl:sort select="group_id" data-type="number" />
</xsl:apply-templates>

Next, within the called template, I need to loop through all the <details> and add row numbering information but add an extra row at the end of each group.

So hope to see:
<output row="1">250</output>
<output row="2">250</output>
<output row="3" />
<output row="4">300</output>

I tried to solve this problem as following:

Please consider the following example.

Java extension class:

public class Iterator {
    private static int i = 0;

    public static int next() {
        i++;
        return i;
    }
}


XSLT stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                       xmlns:java="http://xml.apache.org/xalan/java"
                       exclude-result-prefixes="java"
                       version="1.0">

<xsl:output method="xml" indent="yes" />

<xsl:key name="by-group" match="result" use="details/group_id" />

<xsl:template match="sample">
    <sample>
        <xsl:for-each select="result[generate-id() = generate-id(key('by-group', details/group_id)[1])]">
            <xsl:sort select="details/group_id" data-type="number" />
            <xsl:variable name="x" select="position()" />
            <xsl:for-each select="key('by-group', details/group_id)">
                <output row="{java:Iterator.next()}"><xsl:value-of select="details/group_id" /></output>
            </xsl:for-each>
            <xsl:if test="$x != last()">
                <output row="{java:Iterator.next()}" />
            </xsl:if>
        </xsl:for-each>
    </sample>
</xsl:template>

</xsl:stylesheet>

Using Xalan-J (ver 2.7.0), when the above stylesheet is applied to the XML:

<sample>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>300</group_id>
        </details>
    </result>
    <result>
        <details>
            <group_id>250</group_id>
        </details>
    </result>
</sample>

The output produced is:

<?xml version="1.0" encoding="UTF-8"?>
<sample>
    <output row="1">250</output>
    <output row="2">250</output>
    <output row="3"/>
    <output row="4">300</output>
</sample>

David Carlisle provided a nice solution, written in pure XSLT 1.0:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
    <xsl:key name="g" match="group_id" use="." />
    <xsl:output indent="yes"/>

    <xsl:variable name="g" select="/sample/result/details/group_id[generate-id()=generate-id(key('g',.))]"/>

    <xsl:template match="sample">
        <xsl:for-each select="$g">
            <xsl:sort select="."/>
            <xsl:variable name="p" select="position()-1"/>
            <xsl:variable name="c" select="count(key('g',$g[.&lt;current()]))"/>
            <xsl:for-each select="key('g',.)">
                <output row="{$c+$p+position()}"><xsl:value-of select="."/></output>
            </xsl:for-each>
            <xsl:if test="position()!=last()">
                <output row="{$c+count(key('g',.))+$p+1}"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>



Home


Last Updated: May 26, 2007