Implementing DISelect using XSLT, WURLF and Java Servlets
When you read the DISelect specification you can’t help but notice the similarity of the syntax with XSLT: conditionals, variables, XPath expressions, attribute value templates, etc. So it’s easy to imagine implementing DISelect by transforming documents to XSLT stylesheets. It seems it’s just a matter of transforming <sel:if> to <xsl:if> and so on, and implement the access functions as XPath extensions. This is what I started to do, thinking it would be easy. It wasn’t, but I learned a lot in the process. Here are a few details.

The idea is that a web page containing DISelect markup is statically transformed into an XSLT stylesheet (a “literal result element as stylesheet”, in XSLT parlance), and put on the web server. The generated stylesheet, which implements the content adaptation as described in DISelect, is run when the document is requested by a browser. In principle the adaptation can be performed at any stage between the server and the browser. Here I did it on the server, because it’s much easier to have XSLT run there.
The static transformation: XSLT
There are a few differences in the semantics of DISelect and XSLT. <xsl:choose> is not quite like <sel:select>, for instance. This makes the mapping not that straightforward, but not too difficult (about 70 lines of XSLT). One major difference between DISelect and XSLT is that DISelect allows changing the value of variables, which XSLT doesn’t allow. So I cheated and used a non-standard element in the saxon XSLT engine that does allows changing variable values. Therefore,
<sel:variable ref="x" value="1"/>
becomes
<saxon:assign name="x" select="1"/>
(Note that it’s actually possible to avoid saxon:assign but one would need to do pretty complicated stuff, including implementing an XPath parser in XSLT, which is crazy.)
Dynamic Adaptation: Saxon, WURLF
The Jigsaw web server is used to run an instance of a Java Servlet which calls saxon to run the dynamic adaptation. When the document is requested, the servlet runs the XSL file and the result is sent back to the browser.
What remains is DIselect access functions, which I implemented in Java as standard XPath extension functions. An example is cssmq-width(), which returns the client device’s screen width, typically used in statements such as:
<sel:if expr="cssmq-width() > 200">
<img xsrc="picture.jpg" mce_src="picture.jpg" alt="photo"/>
</sel:if>
Obviously, if the access functions are going to be executed on the server, it needs data about the client which is requesting the document, in order to figure out the values returned. This is where we use WURFL, a big XML file that contains a lot of data on plenty of mobile devices. It comes with a Java API, which allows you to query device properties by passing the User-Agent string of the HTTP GET request originating from that device. It’s quite easy to use. However, about 75% of the debugging that this stage took was because of me misspelling WURFL and writing WURLF. I just hate that name now.
Does it work?
With desktop browsers, definitely not. WURFL gets it wrong. Or maybe I’m not using it right. Firefox or Konqueror are thought to be Netfront. Opera is thought to be Nokia 6230. WURFL claims it’s for “wireless devices”, which is missing the point that the line between conventional computers and mobile devices is getting thinner and thinner. Anyone who would want to write a service for any device would find WURFL insufficient, because it’s only telling you about mobile phones and PDAs.
On mobile phones and PDAs, here’s what WURLF found:
- Tréo 650 with Blazer 3.0 (default browser): WURFL finds it’s a Tréo 650
- Tréo 650 with Xiino/3.1: WURFL returns “generic”
- Tréo 650 with Opera Mini: WURFL thinks it’s a Nokia 6230
- Motorola V220 with default browser: WURFL correctly guessed.
That’s not bad. Opera Mini is tricky since HTTP requests are proxied through opera servers. Xiino is an obscure browser and WURFL cannot know about everything. The next problem is that WURFL properties don’t match DISelect functions (copied from CSS media queries, actually). For instance, WURLF doesn’t have a property for the screen’s size width and height in real length units (cm, mm, etc.). Reciprocally, WURFL has “xhtml_table_support” which seems pretty essential but doesn’t feature in DISelect. Admittedly, both are extensible, but that’s not enough to make them interoperate.
Obviously a wider device repository is needed, to which all browser manufacturers can contribute. Well, guess what, there’s a W3C MWI work item for that, called Device Description Repository, and the Working Group is holding a workshop on it as I’m writing these lines.
Notes
Why Jigsaw and not Tomcat? Because Jigsaw is home-brewed at W3C and Yves Lafon was kind enough to help me set it up locally. It seems it would be very easy to port it to Tomcat, which I might do if I’m going to distribute this to anyone.
Why XSLT and not PHP? I thought about transforming DISelect source files into PHP (sel:if to if(), etc.) but didn’t know enough that you can’t do that, at least not XSLT because PHP isn’t well-formed. At least the PHP generated here wouldn’t be.
What’s missing: access to the delivery context via XPath. E.g. <sel:value ref=”/system/os/version/”/> Seems feasible if the server manages to maintain a source XML file with the correct values for the requesting client. Also, we don’t do DISelect events at all, since XSLT doesn’t have events and always recovers silently. Therefore this implementation will never be able to claim compliance to DISelect.
Client-side transformation? You’d have to find a browser with an XSLT engine that lets you have extension-functions and assign variables. As far as I know there isn’t any that does assigning, which is a shame (or maybe I’m just pushing XSLT a bit too far).
If I had to do this seriously, I would do it in PHP, which is widely implemented and would free me from XSLT, Saxon and Java. The offline transformation (from DISelect source to PHP file) could be done in any language that has a decent DOM API. The service could then run on any off-the-shelf Apache+PHP. I would try to do a better job than WURFL by analysing the User-Agent string a bit better, at least to try and find out if the client is a desktop computer.
Please email me for details or code.