Introducing Code Generation - Plain-Vanilla Code
(Page 5 of 13 )
Another fundamental issue plagues the CodeDOM. For the CodeDOM to create code valid in all languages, the abstraction can only work with elements found in all languages. You’re forced to write code from a least common denominator approach, using only elements that appear in all languages. For example, you can’t use VB .NET’s project-level namespaces or WithEvents, With, or Event. You have to understand subtle but important differences in constructs such as between VB .NET’s Imports and C#’s using. There’s no built-in support for preprocessor directives, including region directives. The CodeDOM doesn’t even support all features currently provided by both languages (see Chapter 3 for details). Also, it isn’t clear how frequently the CodeDOM will be updated with new language features as .NET evolves because the CodeDOM wasn’t updated to reflect new language features between .NET 1.0 and 1.1 (Visual Studio 2002 and 2003).
And to make this worse, I know of no reference you can rely on to list all of the caveats and special considerations (except the ones I discuss in Chapter 3 and Appendix D). The result is that not only do you have to write plain-vanilla code, but you have to think that way. This effort is only justified if your job is specifically to create code in multiple languages and you can afford to ignore some of the best features in each language.
Generating “Hello World” via XSLT Templates The third approach is relatively novel in .NET. This is code generation using XSLT templates. XSLT is a template language designed to create any type of text, XML, or Hypertext Markup Language (HTML) output from XML input. Because source code is text output, XSLT can create source code.
The XSLT language might be new to you, and it might appear to be a strange paradigm. It presents a new way of thinking because it’s explicitly designed from the ground up to apply patterns to information in order to transform information into text. Because metadata is information and source code is text, XSLT does exactly the job presented by code generation.
***********
TIP Appendix A introduces XML, XPath, XML Schema Definition (XSD), and XSLT technologies targeted to what you need for code generation.
Understanding the Benefits of XSLT Code Generation
XSLT code generation presents a couple of compelling benefits. First, the template is closer in appearance to your output code. You can create a functioning sample class, copy code from the sample class into the XSLT template shell, and add tags to indicate the variable content that depends on metadata information. The templates are far more readable, editable, and easily searchable for .NET elements. It’s much easier to segment processing to isolate complex sequences and mimic the resulting document sufficiently to support continued maintenance of templates.
When you’re working with code templates, searches can be problematic, especially if you’re using CodeDOM or brute force because you’re overlaying the syntax of the output code with the code that runs the template. For example, regions are your primary tool in correlating output with the part of the template that produces it. If you search for the word region, your results will include the regions that are part of the generating code itself, as well as those destined for your target output. String concatenations further complicate your searches, especially when your target contains quotes. XSLT avoids these problems almost entirely because the syntax of XSLT differs markedly from the syntax you’re outputting. XSLT is also flexible about the use of single and double quotes, allowing easier handling of nested strings.
An important secondary benefit is that XSLT code generation doesn’t allow you to cheat on the isolation of metadata. Later in this chapter and in Chapter 2, you’ll see the benefits of isolating metadata and its creation. In a brute-force or CodeDOM approach, you could mix running queries that grab fragments of information into the code outputting process. Mixing these two processes makes debugging and reuse difficult. XSLT forces this separation of metadata collection and code generation. You can also supply this separation manually with brute-force or CodeDOM generation.
In Chapter 3 you’ll see generating code as a process that contains many steps, and you’ll see how to orchestrate these steps via a script. I call the tool that runs this script the code generation harness, or just the harness. When you’re working with the code generation harness, you can edit XSLT and regenerate without stopping and restarting the harness. Because brute-force and CodeDOM templates are compiled .NET code, the modules containing them are loaded in the harness’s process space. These modules aren’t unloaded, so you have to stop the harness before you compile changes to the templates. Because no method of code generation avoids syntax errors, you’ll be generating code, finding syntax errors, correcting your code templates, and rerunning the generation. The turnaround for fixing common silly mistakes will be significantly faster with XSLT because you don’t need to stop and restart the code generating application.
The core of XSLT is XPath, which you have to learn anyway. XPath is the language used to query XML within .NET (and most other technologies). Even if you’re doing code generation through one of the other techniques, you’ll be learning and using XPath if you’re using XML metadata.
Running the Template
To create the output, the example calls a generic GenerateViaXSLT method, passing the name of the template:
' Generate Hello World via XSLT Template GenerateViaXSLT(IO.Path.Combine(xsltDir, "HelloWorld.xslt"), Nothing, _ IO.Path.Combine(outputDir, "HelloWorldViaXSLT.vb"))
*************
TIP Shared methods of the IO.Path class allow you to do various cool things with file paths. In this case, IO.Path.Combine combines paths, letting you ignore whether intervening backslashes are handled correctly and other details.
GenerateViaXSLT wraps the powerful XSLT features of the System.Xml.Xsl namespace. This method takes the name of the XSLT file, an XML document to transform, and the name of the output file. The method that provides the outputting is generic because the entire template is contained in the XSLT file. You can pass parameters, but the “Hello World” program requires neither an input XML file nor parameters:
Private Shared Sub GenerateViaXSLT( _
ByVal xsltFileName As String, _
ByVal xmlMetaData As Xml.XmlDocument, _
ByVal outputFile As String, _
ByVal ParamArray params() As xsltparam)
Objects created from several XML and XSLT classes handle the translation. The XslTransform object performs the actual translation. XSLT works most efficiently if the input XML is an XPathNavigator because XPathNavigator objects have internal features for efficient XPath access. The StreamWriter takes the output of the transformation:
Dim xslt As New Xml.Xsl.XslTransform Dim xNav As Xml.XPath.XPathNavigator Dim streamWriter As IO.StreamWriter
This method creates an XML document if you don’t pass one because the transformation needs at least an empty XML document for input. It also creates XSLT parameters if you pass any parameter values:
Dim args As New Xml.Xsl.XsltArgumentList Dim param As XSLTParam
Try If xmlMetaData Is Nothing Then xmlMetaData = New Xml.XmlDocument End If
For Each param In params args.AddParam(param.Name, "", param.Value) Next
The method then creates the navigator from the XML document and creates the StreamWriter connected to the output file:
xNav = xmlMetaData.CreateNavigator() streamWriter = New IO.StreamWriter(outputFile)
Once everything is ready, the method just loads the XSLT file and performs the transform. When the call to the Transform3 method is complete, the code is output although you won’t see it if you forget to flush the stream:
xslt.Load(xsltFileName) xslt.Transform(xNav, args, streamWriter, Nothing)
As with other approaches, flush and close before you leave the method. Placing this in the Finally block ensures it can’t be bypassed:
Finally
If Not streamWriter Is Nothing Then streamWriter.Flush() streamWriter.Close()
End If End Try
End Sub
Creating the Template
XSLT templates intersperse the .NET output code with XSLT directives. Not only is this more readable, but I created it by copying the target code into the stylesheet. Because I had already modified my default template so it contained the header I use, creating this template took less than 60 seconds.
***********
3. The Transform method underwent considerable revision between .NET 1.0 and .NET 1.1. The changes mostly involved supporting XML resolvers. You probably won’t need to be concerned with resolvers when doing code generation and can pass Nothing for the resolver.
******************
TIP See Appendix A for information about how you modify your default XSLT template to provide the starting point you want.
Each XSLT stylesheet starts with some standard XSLT “goo,” including a legal XML header. Although it isn’t strictly required, XSLT is a specialized version of XML, so it’s a good idea to include a legal XML header. XML requires a single root element, and for XSLT stylesheets this is the xsl:stylesheet4 element. This element contains attributes defining the namespaces for the transformation. Appendix A discusses namespaces and the rest of the file opening goo in detail as well as shows you how to automate the header creation via your default template so you can fix it once and forget about it:
<?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" encoding="UTF-8" indent="yes"/> <xsl:preserve-space elements="*" />
Stylesheets contain templates, and templates do the real work of XSLT code generation. I copied the sample code into the single template in this stylesheet. XSLT will output verbatim any text that appears in a template that isn’t an XSLT directive:
<xsl:template match="/"> Option Strict On Option Explicit On
Imports System
' Class Summary:
Public Class TargetOutput
#Region "Public Methods and Properties" Public Shared Sub Main() Console.WriteLine("Hello World") End Sub #End Region
End Class </xsl:template> </xsl:stylesheet>
***************
4. xsl:transform is synonymous with xsl:stylesheet, and you can use either as the stylesheet root. I standardize on xsl:stylesheet.
Because XSLT is well-formed XML, template and stylesheet closing tags appear at the end of the file. That’s it. That’s all there is to XSLT code generation. (Okay, there’s really more to it when you’re generating real-world code with tokens and internal logic, but that’s a start at seeing how you can copy code into your templates.)
This chapter is from Code Generation in Microsoft .NET by Kathleen Dollard (Apress, 2004, ISBN: 1590591372). Check it out at your favorite bookstore today.
Buy this book now. |
Next: Picking the Right Mechanism >>
More .NET Articles
More By Apress Publishing