Next
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 (Anonymous), 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 developing creativity. XSLT can help by acting as a simple code generator, minimizing the tedious part of the developer'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 >
<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 developer 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>
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.
|