.NET
  Home arrow .NET arrow Page 7 - Outputting Code
ASP Free Forums 
.NET  
ASP  
ASP Code  
ASP.NET  
ASP.NET Code  
BrainDump  
C#  
Code Examples  
Database  
Database Code  
IIS  
Microsoft Access  
MS SQL Server  
Visual Basic.NET  
Windows Scripting  
Windows Security  
XML  
ASP Web Hosting  
ASP.NET Web Hosting 
Mobile Linux 
App Generation ROI 
Windows Web Hosting
 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
.NET

Outputting Code
By: Apress Publishing
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 3 stars3 stars3 stars3 stars3 stars / 5
    2004-10-04

    Table of Contents:
  • Outputting Code
  • Understanding Script Directives
  • Understanding Other Features of the Generation Harness
  • Extending the Harness
  • Examining Code Generation Mechanics
  • Exploring Details of XSLT Code Generation
  • Creating Named Templates
  • Creating Match Templates
  • Supporting Stylesheets
  • Exploring Details of Brute-Force Generation
  • Creating a Class
  • Creating the Support Template
  • Understanding Types
  • Building a CodeDOM Graph
  • Building the Code Structure
  • Outputting Assignments
  • Creating Arrays
  • Exploring Other Features
  • Working with the CodeDOM

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Outputting Code - Creating Named Templates


    (Page 7 of 19 )

    Named templates can contain simple or complex output. To create the first named template, create a new xsl:template within your stylesheet and copy the code for the first region into it. It’ll look like this:

    <xsl:template name="CollectionConstructors">
    #Region "Constructors"
      Protected Sub New()
        MyBase.New(" OrderCollection")
      End Sub
    #End Region
    </xsl:template>

    Look for anything that wouldn’t be valid in another output file—all the stuff that changes. The only changing value here is the text OrderCollection. You can replace this with an xsl:value-of directive supplying the SingularName attribute. The resulting template is as follows:

    <xsl:template name="CollectionConstructors">
    #Region "Constructors"
      Protected Sub New()
        MyBase.New("<xsl:value-of  select="@SingularName" />Collection")
     End Sub
    #End Region
    </xsl:template>

    Named templates aren’t always this simple to create. When you copy in the Public and Friend… region of the Orders.vb file, you have this:

    <xsl:template name="PublicAndFriend">
    #Region "Public and Friend Properties, Methods and Events"
    Public Overloads Sub Fill( _
    ByVal OrderID As System.Int32, _
    ByVal UserID As Int32)
    OrderDataAccessor.Fill(Me, OrderID, UserID)
    End Sub
    #End Region
    </xsl:template>

    You often need to know the intent of the various aspects of your sample file. In this case, OrderID is the primary key of the table, and the application passes a UserID to all Fill methods. This is complicated by the fact that Northwind doesn’t contain a primary key for all tables.

    NOTE: I didn’t see the problem with missing primary keys in my crystal ball. I created the template without worrying about this and found compiler errors when there weren’t any primary keys.

    TIP: Code generation is easier if you use a single-column primary key on all tables.

    Variables can simplify processing, but XSLT variables are weird critters. You assign a value when you create them, and then you can never change this value. That doesn’t sound like a variable in any language I’ve used, but it’s consistent with some basic principles of XSLT (discussed further in Appendix A). This PublicAndFriend template uses variables of two different types. The first is a string that contains the name of the primary key. An XPath statement retrieves the Name attribute of the TableConstraint element’s PrimaryKey child element of the current node (the table). The context for this XSLT is the table node:

    <xsl:template name="PublicAndFriend">
    #Region "Public and Friend Properties, Methods and Events"<xsl:text/> <xsl:variable name="primarykeyname" select="dbs:TableConstraints/dbs:PrimaryKey/dbs:PKField/@Name" />

    To create a corresponding method declaration, you need the type, as well as the name, of the primary key. To find this, you can use the name of the primary key, now held in the $primarykeyname variable, to retrieve the matching node within the column definitions (see the XML metadata in Listing 3-1). This code assigns a single node to the $primarykey variable. This node is the TableColumn node corresponding to the primary key (context is the table node):

    <xsl:variable name="primarykey"  select="dbs:TableColumns/dbs:TableColumn[@Name=$primarykeyname]" />

    NOTE: This template assumes you’ll use single-column keys and ignores any additional keys. You’ll learn later how to deal with complex primary keys.

    TIP: Use the column node when you need any details on the column, such as its type.

    The table may have no primary key, and in that case, the class needs a Fill method with only the UserID parameter. The directives between xsl:if and /xsl:if execute only if there are any primary key nodes. When this block executes, output from this block includes the name and type of the primary key:

    Public Overloads Sub Fill( _<xsl:if test="$primarykey">
    ByVal <xsl:value-of select="$primarykey/@Name"/> As <xsl:text/> <xsl:value-of select="$primarykey/@NETType"/>, _</xsl:if>
    ByVal UserID As Int32)

    NOTE: When testing a variable containing a set of nodes, the test results in true if there are any nodes in the set.

    Outputting the call to the Fill method presents similar issues and solutions. The SingularName attribute of the table node makes up part of the name of the data access layer class. The xsl:if directive is again used to include the primary key field name as a parameter only if it exists:

    <xsl:value-of select="@SingularName"/>DataAccessor.Fill(Me<xsl:text/> <xsl:if test="$primarykey">, <xsl:value-of select="$primarykey/@Name"/>
    </xsl:if>, UserID)
    End Sub
    #End Region
    </xsl:template>

    Managing Whitespace

    This template also provides good examples of whitespace management. Unfortunately, you have to think about whitespace when you write XSLT templates. You might have thought I was careless (or downright dumb) in the layout of the XSLT directives in the previous section, but this layout provides the correct whitespace in the output.

    Whitespace that’s output is called significant whitespace. Whitespace is significant, meaning that it’s output, when the whitespace is adjacent to any text that’s output. When any two XSLT directives are adjacent to each other with only whitespace between, the processor assumes that intervening whitespace was for your convenience in reading the template and ignores it. That’s called insignifi cant whitespace. Put another way, only whitespace that isn’t between XSLT directives is output.

    NOTE: The meaning of whitespace is different in the generated VB .NET and C# code. In VB .NET, the type of whitespace often has meaning, particularly the Carriage Return/Line Feed (CRLF) character at the end of each line, the spaces before line continuation characters, and so on. C# has few places where the type of whitespace has semantic meaning for the compiler. However, programmers reading the code will appreciate your efforts at whitespace management in either language.

    Because of whitespace behavior, if you write a method declaration such as this:

    Public Overloads Sub Fill( _
    <xsl:if test="$primarykey">
    ByVal <xsl:value-of select="$primarykey/@Name"/> As System.Int32, </xsl:if>

    then two new lines (two CRLF pairs) will be output, one after the underscore and the other before the ByVal. The result would be this:

    Public Overloads Sub Fill( _

        ByVal OrdersID" As System.Int32

    with the blank line causing a VB .NET compiler error. Take another look at the template lines if you don’t see why this problem would occur.

    Sometimes you want to break a line in the template at a location illegal in the output, such as this example. I could just toss in another line-continuation underscore, but that would output ugly code, and I want output to be easy to read, without artifacts of code generation. In the earlier example, I swallowed my desire to put the xsl:if test on a separate line. Alternatively, I could’ve tossed in an empty xsl:text directive, which is simply a meaningless XSLT tag in this context. The xsl:text directive can appear at either the end or beginning of the next line, depending on which is needed to wrap the offending whitespace between two XSLT directives. In this case, you can put it either after the underscore or before the ByVal, but not both:

    Public Overloads Sub Fill( _ <xsl:text/>
    <xsl:if test="$primarykey">
    ByVal <xsl:value-of select="$primarykey/@Name"/> As System.Int32</xsl:if>

    TIP: Whether you move directives onto the ends of lines such as this approach or use xsl:text at the start of lines is sometimes matter of style. But in other cases, conditional and looping constructs dictate where you place the xsl:text element.

    The XSLT processor generally ignores whitespace within directives. I have the extra demands of making text readable on this page because you can’t scroll the page if the text gets too wide. This results in extra xsl:text elements in the book’s code, but it also gives me a chance to clarify where you can wrap within directives. You can generally split anywhere in a directive other than in a quoted string or within an element or attribute name, such as:

    <xsl:variable name="primarykeyname" select="dbs:TableConstraints/dbs:PrimaryKey/dbs:PKField/@Name" />

    Sometimes you’ll need to output whitespace between directives. You might need to add tabs, spaces, or new line characters. These need to be escaped character sequences placed within an xsl:text directive. Appendix A shows examples of adding whitespace using escape characters. Support.xslt also contains a template that outputs new line characters.

    XSLT whitespace is a pain. But as you become familiar with it, you’ll find it workable, even if your XSLT templates will never have the grace of alignment you expect from your generated files.

    TIP: Solve whitespace issues so your generated code is logically aligned. This will save lots of problems in reading your code later.

    Using Looping, Conditional Variables, and Other Fun Stuff

    The ClassLevelDeclaration named template illustrates a couple of new things. The code target starts with this:

    #Region "Class Level Declarations"
    Protected mCollection As OrderCollection
    Private Shared mNextPrimaryKey As Int32 = -1

    Private mOrderID As System.Int32

    This goes on with a full list of fields that correspond to the columns in the table.

    The first three lines of the template that outputs this code uses techniques you’ve already seen. You can output the list of the fields using an xsl:for-each directive. The xsl:for-each directive loops through each node in the specified set and outputs the class-level declarations:

    <xsl:template name="ClassLevelDeclarations">
    #Region "Class Level Declarations"
      Protected mCollection As <xsl:value-of   select='@SingularName'/>Collection
      Private Shared mNextPrimaryKey As Int32 = -1
      <xsl:for-each select="dbs:TableColumns/*">
        <xsl:if test="string-length(@NETType)>0">
      Private m<xsl:value-of select="@Name" />
           <xsl:text/> As <xsl:value-of select="@NETType" />
       </xsl:if>
     </xsl:for-each>
    #End Region
    </xsl:template>

    NOTE: In every case where an xsl:for-each directive works, an xsl:apply-template with a mode also works, and vice versa. In your favorite programming language, you gained confidence in your proficiency at separating parts into subroutines. Similarly, you’ll quickly become confident in determining whether to use an xsl:for-each or an xsl:apply-templates. If you avoid xsl:apply-templates, you’ll build difficult-to-read XSLT templates with deeply nested loops. But, too many templates containing only one or two lines can also be difficult to read. As a rough guideline, if the match template would contain fewer than three lines, or the calling template would be reduced to fewer than about five lines, I use xsl:for-each. Otherwise, I use xsl:apply-templates.

    The xsl:if handles the case where the metadata doesn’t include a NETType attribute. The NETType attribute is derived from the SQL type during metadata extraction, and the extraction process doesn’t yet support some SQL types such as image and varbinary types that exist in the Northwind database. They don’t support them because I don’t anticipate displaying employee pictures. Because the metadata contains empty strings for these unsupported types, the generated source code would result in .NET compiler errors. Again, my crystal ball didn’t tell me this. I ran the template, found complier errors, and tracked them back to this problem. 

    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.

    More .NET Articles
    More By Apress Publishing


     

    .NET ARTICLES

    - Using CrystalReportViewer to Display Crystal...
    - Creating Summary .Net Crystal Reports
    - More on Commands, Input and the WPF
    - Grouping and Aggregating When Querying LINQ ...
    - Commands, Input and the WPF
    - Keyboard and Ink Input with WPF
    - Mouse Input and the WPF
    - Input with Windows Presentation Foundation
    - Introducing LINQ with XML and Databases
    - An Introduction to LINQ
    - Querying LINQ to SQL: Basics
    - Completing a Simple Storefront with LINQ
    - Knowing Your Environment: the System.Environ...
    - Creating the Home Page for a Simple Storefro...
    - LINQ Quickly with Language Integrated Queries





    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 1 hosted by Hostway
    Stay green...Green IT