[xsl] Discrepancy in handling current() in a pattern

Subject: [xsl] Discrepancy in handling current() in a pattern
From: Alain <alainb06@xxxxxxx>
Date: Wed, 10 Jun 2009 21:32:03 +0200
Hi experts,

here is what I am trying to do.


*"Business" requirements:*
-------------------------
I want to display a table (Sudoku-like) in a browser.
The values of the cells in the tables are stored in a XML file.
The browser receives directly the XML and has to display it nicely.
[Meaning I am limited to XSLT 1.0]

Every cell in the table as "coordinates" :
x, y, z
x: abcissa or column number (starts at 1, increment by 1)
y: ordinate or row number (starts at 1, increment by 1)
z: zone (as in Sudoku zones, eg a 3x3 square on an 9x9 Sudoku)

The table has to be displayed according to x, y -easy part!-
Lines has to be displayed, horizontally or vertically when there is a
change of zone

Typical Sudoku would then be
(I try some ASCII Art here, put your mail client in "fixed font")

1 2 3 | 4 5 6 | 7 8 9
2 3 1 | 5 6 4 | 9 7 8
3 1 2 | 6 4 5 | 8 9 7
=========+===========+==========
4 5 6 | 7 8 9 | 1 2 3
5 6 4 | 9 7 8 | 2 3 1


And so on.
So you have 4 types of cells:
- those who have a line on the left
- those who have a line on the top
- those who have both lines
- those who have no line at all

In the above figure, we have 6 "zones" separated by horizontal or
vertical lines.


*The XML*
-------

<?xml version="1.0" encoding="utf-8"?>
<sudoku>
<cell x="1" y="1" z="1"></cell>
<cell x="2" y="1" z="1"></cell>
<cell x="3" y="1" z="1">7</cell>
<cell x="4" y="1" z="2">2</cell>
<cell x="5" y="1" z="2"></cell>
<cell x="6" y="1" z="2"></cell>
<cell x="7" y="1" z="3"></cell>
<cell x="8" y="1" z="3"></cell>
<cell x="9" y="1" z="3"></cell>

<cell x="1" y="2" z="1"></cell>
<cell x="2" y="2" z="1"></cell>
<cell x="3" y="2" z="1"></cell>

<!-- and so on -->

</sudoku>




*The XSL*
-------
<?xml version='1.0' encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:output method="html" encoding="utf-8"/>

<xsl:template match="/sudoku">

<!-- HTML blah-blah-->
<html>
<head>
<title>Displaying Sudoku</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="sudostyle.css"/>
</head>

<body bgcolor="#F9F9FF">

<p class="a">Puzzle</p>
<table cellspacing="0">

<!-- Here, we select first cell of each row and apply the row template-->

<xsl:apply-templates select="cell[@x = '1']" mode="row">
<xsl:sort select="@y" data-type="number"/>
</xsl:apply-templates>
</table>

</body>
</html>
</xsl:template>

<xsl:template match="cell" mode="row">
<tr>
<xsl:apply-templates select="../cell[@y= current()/@y]" mode="cell">
<xsl:sort select="@x" data-type="number"/>
</xsl:apply-templates>
</tr>
</xsl:template>


<!-- First "stack" of matching for vertical lines -->
<!-- This matches cells that have a vertical lines on their left -->

<xsl:template match="cell[@x != '1' and @z != ../cell[@y =
current()/@y][@x= current()/@x - 1]/@z]" mode="cell" priority="2">
<xsl:apply-templates select="." mode="cell-2">
<xsl:with-param name="normal" select="'d'"/>
<xsl:with-param name="border" select="'f'"/>
</xsl:apply-templates>
</xsl:template>


<!-- And so, this matches other cells (those who do NOT have the
vertical line) -->

<xsl:template match="cell" mode="cell" priority="1">
<xsl:apply-templates select="." mode="cell-2">
<xsl:with-param name="normal" select="'c'"/>
<xsl:with-param name="border" select="'e'"/>
</xsl:apply-templates>
</xsl:template>


<!-- Second "stack" of matching for horizontal lines -->
<!-- This matches cells that have a horizontal lines on their top -->

<xsl:template match="cell[@y!='1' and @z != ../cell[@y = current()/@y
-1][@x= current()/@x]/@z]" mode="cell-2" priority="2">
<xsl:param name="border"/>

<td class="{$border}">
<xsl:apply-templates select="." mode="inner"/>
</td>
</xsl:template>


<!-- And so, this matches other cells (those who do NOT have the
horizontal line) -->

<xsl:template match="cell" mode="cell-2" priority="1">
<xsl:param name="normal"/>

<td class="{$normal}">
<xsl:apply-templates select="." mode="inner"/>
</td>
</xsl:template>


<!-- Here, we display the content of the cell -->

<xsl:template match="cell" mode="inner">
<p class="b"><xsl:value-of select="."/></p>
</xsl:template>

</xsl:stylesheet>



*Comments:*
--------
Of course, the 4 types of cells are just mapped to 4 different CSS
styles (class c,d,e,f).
Problems arose because I remembered the lesson and I tried to avoid the
ugly case/otherwise and made templates to sort out cells with borders.


Results:
--------
Firefox 3 : works fine
I.E. 6 or 7 : says that current() is not allowed in a pattern

Xalan : works fine (no error or warning)

Saxon 9 (runing as a XSLT1.0 stylesheet or as an XSLT2.0, makes no
difference) reports a recoverable error on the match. The error seems to
be that current() is of type untypedAtomic and node() type is expected.
As we are matching nodes, I assume the cast() works and it can be recovered.



So my questions:
---------------
1) Because of the discrepancy I noticed between Firefox and I.E, my
first question is: "Is current() allowed or not in an XSLT1.0 pattern?"
(or maybe it is "implementation defined or dependant" but XSLT1.0 is
unclear about that... or I couldn't find it!)


2) Is is allowed in XSLT2.0 -should be because Saxon do not complain
about current() itself, but about its type-, and what is the correct
expression to remove the recoverable error?
/Although I would probably try to code this puzzle differently in 2.0,
using for example a for-each-group/


3) Do you think of a better way to program this puzzle that should be
compatible with both I.E. and Firefox, apart from doing a unique pattern
with an ugly stack of 2 nested cases/otherwise.

Current Thread