Book HomeJava and XSLTSearch this book

8.5. XSLT as a Code Generator

For performance reasons, EJB components typically return dependent objects rather than many individual fields. These are implemented as read-only classes that encapsulate a group of related fields. Borrowing an example from Enterprise JavaBeans by Richard Monson-Haefel (O'Reilly), Example 8-13 shows a typical dependent object.

Example 8-13. Address.java

public class Address implements java.io.Serializable {

    private String street;
    private String city;
    private String state;
    private String zip;

    /**
     * Construct a new dependent object instance.
     */
    public Address(String street, String city, String state, String zip) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;

    }
    
    public String getStreet( ) {
        return this.street;
    }
    
    public String getCity( ) {
        return this.city;
    }
    
    public String getState( ) {
        return this.state;
    }
    
    public String getZip( ) {
        return this.zip;
    }
}

Now, rather than containing numerous fine-grained methods, an entity bean can provide a single method to retrieve an instance of Address. This reduces load on the network and database and makes the code somewhat easier to understand. As you can see, the Address class is very straightforward. It has a constructor that initializes all fields and a series of get methods.

Although Address is small, some dependent objects may have dozens of fields. These are tedious to write at best, resulting in a typing exercise rather than programming creativity. XSLT can help by acting as a simple code generator, minimizing the tedious part of the programmer's job. AddressDO.xml, shown in Example 8-14, contains the data that will feed into our code generator.

Example 8-14. AddressDO.xml

<?xml version="1.0" encoding="UTF-8"?>
<dependentObject class="Address">
    <property name="street" type="String" getter="getStreet"/>
    <property name="city" type="String" getter="getCity"/>
    <property name="state" type="String" getter="getState"/>
    <property name="zip" type="String" getter="getZip"/>
</dependentObject>

The XML data is obviously much shorter than the generated code, and the difference is magnified for larger dependent objects with many fields. The <dependentObject> element contains a list of <property> elements, each of which defines the field name, datatype, and get method name. Now that the data is captured in a well-defined XML format, a DTD or Schema can be used to perform validation. A really ambitious programmer might want to create a simple GUI front-end that allows graphical editing of the <dependentObject> structure.

An XSLT stylesheet performs the actual code generation. The output method should be set to text, and particular attention must be given to whitespace. With HTML or XHTML output, whitespace is largely irrelevant. Since browsers collapse multiple spaces and linefeeds into a single space, the XSLT stylesheet can be indented and spaced however you like. But with a code generator, formatting is a much higher priority. This can lead to stylesheets that are much harder to read, which is the main drawback of using XSLT as a code generator. Example 8-15 shows the dependent object code generator stylesheet.

Example 8-15. dependentObject.xslt

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="className" select="/dependentObject/@class"/>
    <!--
    ********************************************************************
    ** Generate the class skeleton. Other templates will generate
    ** portions of the class.
    *****************************************************************-->
    <xsl:template match="/dependentObject">public class <xsl:value-of
             select="$className"/>
       <xsl:text> implements java.io.Serializable {
</xsl:text>
    <xsl:apply-templates select="property" mode="generateField"/>
    <xsl:text>

    /**
     * Construct a new dependent object instance.
     */
    public </xsl:text>
            <xsl:value-of select="$className"/>(<xsl:apply-templates 
                 select="property" mode="generateConstructorParam"/>
            <xsl:text>) {
</xsl:text>
            <xsl:apply-templates select="property" 
                 mode="generateInitializers"/>
    }

    <xsl:apply-templates select="property" mode="generateGetter"/>
}
    </xsl:template>

    <!--
    *****************************************************************
    ** Generate a private field declaration.
    **************************************************************-->
    <xsl:template match="property" mode="generateField">
    private <xsl:value-of select="@type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@name"/>;</xsl:template>
    
    <!--
    *****************************************************************
    ** Generate a "get" method for a property.
    **************************************************************-->
    <xsl:template match="property" mode="generateGetter">
    public <xsl:value-of select="@type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@getter"/>( ) {
        return this.<xsl:value-of select="@name"/>;
    }
    </xsl:template>
    
    <!--
    *****************************************************************
    ** Generate one of the constructor parameters.
    **************************************************************-->
    <xsl:template match="property" mode="generateConstructorParam">
        <xsl:text xml:space="preserve"/>
        <xsl:value-of select="@type"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="@name"/>
        <xsl:if test="position() != last( )">, </xsl:if>
    </xsl:template>
    
    <!--
    *****************************************************************
    ** Generate the initialization code inside of the constructor.
    **************************************************************-->
    <xsl:template match="property" mode="generateInitializers">
        <xsl:text xml:space="preserve">        this.</xsl:text>
        <xsl:value-of select="@name"/>
        <xsl:text> = </xsl:text>
        <xsl:value-of select="@name"/>;
    </xsl:template>
</xsl:stylesheet>

This stylesheet produces the code for Address.java. It starts by setting the output method to text and creating a variable for the class name. The variable allows us to avoid typing <xsl:value-of select="/dependentObject/@class"/> whenever the class name is needed.

The <xsl:text> element is used frequently in code-generator stylesheets because it allows for more control over whitespace. In several places, this element is used to introduce linefeeds in the output. For instance:

       <xsl:text> implements java.io.Serializable {
</xsl:text>

Because the closing tag is on the next line, the linefeed character will be preserved faithfully. <xsl:text> is also used to introduce individual spaces:

private <xsl:value-of select="@type"/>
<xsl:text> </xsl:text>
<xsl:value-of select="@name"/>;</xsl:template>

By default, XSLT processors ignore whitespace between two XSLT elements unless some nonwhitespace characters are also present. The private text shown just before <xsl:value-of select="@type"/>, for example, contains nonwhitespace text followed by a space. In this case, the space after the word private will be preserved. But the space between the two <xsl:value-of> elements will be ignored unless it is explicitly preserved with <xsl:text> </xsl:text>.

Getting everything to indent and line up is challenging but is not an insurmountable problem. It usually boils down to a lot of XSLT tweaking until everything looks just right. Using a code beautifier is another option. Products such as JIndent (http://www.jindent.com) can automatically clean up Java code by wrapping long lines, inserting spaces, and putting braces at the correct locations. If you are fortunate enough to have access to a tool like this, you can ignore most whitespace issues in the XSLT and rely on JIndent to fix formatting problems later on.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.