20050919 Monday September 19, 2005

Xalan Text Extension

Just added another example to the Xalan Text Extension that I recovered this weekend. I rememembered that I wanted to use this tool for generating script as well (make sure that the comments are formatted correctly), but I never actually gave it a try. It appears that I needed another feature, so I decided to add that, while I was at it. (You can download the latest version of this tool here.

This is an example showing how it works. The file below is an XML vocabulary to specify a launcher for your Java program. It's actually based on a subset of a DTD that I used in the past to generate Windows .cmd and Unix .sh scripts. The main difference is that this one is extremely simplified; in fact, it doesn't serve any purpose at all, since it assumes that all of the required jars are in the CLASSPATH environment variable. (Duh.)

<?xml version="1.0"?>
<!DOCTYPE launcher [

<!ELEMENT launcher (description?, copyright?, mainClass)>
<!ELEMENT description (#PCDATA)>
<!ELEMENT copyright (year+, holder)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT holder (#PCDATA)>

]>
<launcher>
  <description>This is just an example of a script file, to show how
    the Xalan text extensions might help you to generate script from
    XML; not that generating script is very hard with XSLT, but it can
    be tricky to make sure that it looks acceptable. So we don't want
    to use comment lines of more than 75 characters. Let's see if we
    can somehow accomplish that.</description>
  <copyright>
    <year>2005</year>
    <holder>Wilfred Springer</holder>
  </copyright>
  <mainClass>com.agilejava.foobar.main</mainClass>
</launcher>

Anyhow, the real question is how we can convert this into a script, that looks as if it was written by hand. The XSLT isn't really that hard, but formatting the comments can be tricky. However, with the Xalan Text Extensions it becomes pretty easy:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xalan="http://xml.apache.org/xalan"
  xmlns:text="xalan://com.agilejava.xalan.text.TextExtension"
  extension-element-prefixes="text"
  version="1.0">

  <xalan:component prefix="text"
                   elements="indented">
    <xalan:script lang="javaclass" 
                  src="xalan://com.agilejava.xalan.text.TextExtension"/>
  </xalan:component>
  
  <xsl:output method="text"/>
  
  <xsl:template match="/">
    <text:format break-after="true">#!/bin/sh</text:format>
    <xsl:call-template name="newline"/>
    <xsl:apply-templates select="launcher"/>
  </xsl:template>

  <xsl:template match="launcher">
    <text:format break-after="true" prefix="# " cols="75">
      <xsl:value-of select="normalize-space(description)"/>
    </text:format>
    <xsl:call-template name="newline"/>
    <text:format prefix="# " break-after="true">
      <xsl:apply-templates select="copyright"/>
    </text:format>
    <xsl:call-template name="newline"/>
    <xsl:text>"$JAVA_HOME"/bin/java </xsl:text>
    <xsl:value-of select="mainClass"/>
    <xsl:call-template name="newline"/>
  </xsl:template>

  <xsl:template match="copyright">
    <xsl:text>Copyright (C) </xsl:text>
    <xsl:for-each select="year">
      <xsl:if test="not(position()=1)">
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:value-of select="text()"/>
    </xsl:for-each>
    <xsl:text> </xsl:text>
    <xsl:value-of select="holder"/>
  </xsl:template>

  <xsl:template name="newline">
    <xsl:text>
</xsl:text>
  </xsl:template>
  
</xsl:stylesheet>

If we would apply this template to the file above, then the output would look like this:

#!/bin/sh
 
# This is just an example of a script file, to show how the Xalan text
# extensions might help you to generate script from XML; not that
# generating script is very hard with XSLT, but it can be tricky to make
# sure that it looks acceptable. So we don't want to use comment lines of
# more than 75 characters. Let's see if we can somehow accomplish that.
 
# Copyright (C) 2005 Wilfred Springer
 
"$JAVA_HOME"/bin/java com.agilejava.foobar.main

Note that - in order to have wordwrap for the description - there wasn't actually much I needed to do: I simply generated a text:format element, and added the output of the XPath normalize-space(description) expression as a child. The text:format tag element translates its content into a formatted block of text, making sure that the line length does not exceed 75 characters, and prefixing it with "# ".

<text:format break-after="true" prefix="# " cols="75">
  <xsl:value-of select="normalize-space(description)"/>
</text:format>


( Sep 19 2005, 02:52:21 PM CEST ) Permalink Comments [0]