Tuesday, August 09, 2005

Fun with XSLT - no, really!

I've been having a lot of fun with XSLT recently.

I'm currently working on a customised course for a banking client. (No, not the French one). Over the last couple of years I have been gradually refining a course publishing framework that takes a MindMap and turns it into courseware. The framework works pretty well, but I still need to create lots of graphics.

Trouble is, I hate using drawing editors.

I'd much rather just describe the picture that I want in text and then have a piece of software draw the diagrams for me. Fortunately there are several Open Source tools that can help; one of my favorites is dot (part of the GraphViz package) . Dot reads a text file that describes a directed or undirected graph in the dot language and then lays out the graph for you. It can export a variaty of formats, inclding svg which is what my framework prefers.

The dot language is pretty intuitive, but it's a bit repetitive if you are creating lots of similar diagrams. Since I hate un-necessary repetition, I decided to create a little language for some of the most common diagrams that I needed.

Problem: how should I turn my little language into a dot file? Normally I would use a compiler compiler to do this - fun, but not quite trivial. A few days ago I stumbled on an article by Michael Kay which showed how to use regular expressions and the new unparsed-text capabilities of XSLT 2.0 to turn text files into XML. It's then very easy to get XSLT to generate a new text file in dot format.

Here's a short sample input file:

data xml "XML\ndocument"
text txt "Unparsed text\ndocument"
data out "XML, HTML or text\ndocument"
program xsl "XSL\nStylesheet"
process XSLT "XSLT 2.0\nProcessor"
input from xml to XSLT
unparsed from txt to XSLT
code from xsl to XSLT
output from XSLT to out
Here's the dot language file that's generated:

digraph process{
xml[label="XML\ndocument" shape=box]
txt[label="Unparsed text\ndocument" shape=box color=brown]
out[label="XML, HTML or text\ndocument" shape=box]
xsl[label="XSL\nStylesheet" shape=invhouse]
XSLT[label="{{XSLT|ANT task}|XSLT 2.0\nProcessor}" ]
xml -> XSLT
txt -> XSLT[color=brown]
xsl -> XSLT[color=gray]
XSLT -> out
{ rank=same; txt; xsl; }
{ rank=same; xml; out; XSLT; }
And here's the output as a jpeg image:

No comments: