So, what’s a software developer doing all day? Sometimes, it can be ridiculous. Of course, ridiculous makes for good stories, if you are the type of person who enjoys programming jokes.
It was a sunny day, but the day was long gone, and the night laid its cloth of dark silk over the world, when I cracked my knuckles and straightened my back to face a task that would lead me into the treacherous belly of the cave that is libxslt. The main path was crisp and clear: By the shuffling feet of many adventurers the chalky stone slab that marked the opening of the cave was polished smooth like an old mouse pad. Surely, if I just stayed within the narrow way marked by torches, heeded the warnings scribbled on the entrance of dark and webbed side tunnels, and in general followed the instructions given to me on the internets by my friendly comrades in the past, surely, it would be easy enough to find the grand hall of the dragon, take my piece of the treasure, and return safely to the surface, where my family and friends would wait for me with the spoils of everyday life.
But alas, little did I know that some signs were missing, and not every tunnel that was lit was safe. So here begins my story in which I get lost and come back again, and find myself at the highest level of the cave, adding my warnings to the written history of failures that is clearly visible on the walls. May future adventurous find the way a little better.
It begins, as usual, with a task that is clear enough: Transform a flat section structure to a nested one. So, a document like this:
<h1/> <h2/> <h1/>
Should become this:
<h1> <h2/> </h1> <h1/>
Surely, this has been done before. And surely, Uncle Google comes up with plenty of solutions from the old days. However, these particular solutions relied on the extra powers available in XSLT 2.0, while my loyal toolchain was old and rusty, and only up to XSLT 1.0. I don’t blame it, it has served me well over the years, and it would be cruel and heartless to abandon a dear friend just because of his imperfections. A XSLT 1.0 solution was needed. The library of knowledge that is the Internets sometimes hides its treasures, but with patient research not all hope is lost. My task was called [grouping] with the Munchian method, and with that insight I had a thread that ran through all attempts at [solving this problem] before. My sword thus sharpened, I entered the cave bravely and followed the path quickly and with certainty.
<xsl:key name="next-headings" match="h2" use="generate-id(preceding-sibling::h1)" /> <xsl:key name="next-headings" match="h3" use="generate-id(preceding-sibling::*[self::h1 or self::h2])" /> <xsl:key name="next-headings" match="h4" use="generate-id(preceding-sibling::*[self::h1 or self::h2 or self::h3])" /> ...
It was my pride that struck me down. Looking at the solution of the past, I saw a lack of efficiency. Clearly, the “less-than” operator could be applied with great force, breaking through a wall of the cave and making for a convenient short cut. I started to scratch at the stone:
<xsl:key name="next-headings" match="h2|h3|h4|h5|h6" use="generate-id(preceding-sibling::*[(self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6) and (number(substring(name(), 2)) < number(substring(name(current()), 2)))])" />
The wall opened, and I fell through it into another passage. But this passage had a foul odour. I turned back to return where I came from, but the wall collapsed and made it impossible. I was trapped. It was dark.
I wandered around. Surely, the proper path can not be far away. I tried here and there. In the end, it seemed that the
name(current()) was not right. A
trace() function would give me advice, but my ancient tools did not provide it. However, an [extension interface] allowed me to add this functionality, and suddenly a light cast bright shadows on the wall. The
current() function evaluated to the empty node list “” in the context of a use-expression in the
xsl:key. That was blasphemy. The [ancient scrolls of the XSLT 1.0 Technical Report by the W3C] clearly said:
the expression specified in the use attribute of the xsl:key element is evaluated with x as the current node …
… where x is the matched element. A sudden grief came upon me. The ancient scrolls, the holy scrolls from the highest authority, were failing me. I was lost. With such a rupture in the foundation of my belief, anything could happen. My grief turned to anger, and anger to passion. I broke through the ceiling, and through layers of stone. At the end: blue sky, trees. I ran to the [repair shed of my tool provider], and, with a missionary passion, wanted to tell them about my enlightenment. But there was no living soul. Everybody turned to stone. A mighty volume on the desk was opened on a [page marked in red]. It said:
Bug 607893 – Problem with <xsl:key />
I could not believe my eyes. Anxiously, I read on:
I’m trying to cross-reference elements and found a problem. Here’s the very short XML data:
<test> <h level="1" /> <!-- id1 --> <h level="2" /> <!-- id2 --> <h level="2" /> <!-- id3 -->
Can it be? I have not been the first? How could this happen? > I have no clue what went on. I am suspiciuous though, that it has something to do with function current(). Noooooo! I am not the first victim. I am not the first to be enlightened. Others suffered like me, but at an earlier time. How early? 2010-01-23. Three years ago! I was utterly destroyed. The cave is still there, the dragons loot still has to be retrieved. But next time, there will be no shortcut. There will be no standard to help me. Next time, I will have to be more careful, and tread not from the path that has been smoothed by many adventurers before me. I will add my own marks to the common path, as will those following me. Next time, I will fall in line. : http://www.jenitennison.com/xslt/grouping/ : http://stackoverflow.com/questions/4547271/xslt-moving-a-grouping-html-elements-into-section-levels : http://lxml.de/extensions.html : http://www.w3.org/TR/xslt#element-key : http://xmlsoft.org/XSLT/bugs.html : https://bugzilla.gnome.org/show_bug.cgi?id=607893