.NET
  Home arrow .NET arrow Page 11 - 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  
Silverlight  
Visual Basic.NET  
Windows Scripting  
Windows Security  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
ASP Web Hosting  
ASP.NET Web Hosting 
Windows Web Hosting
 
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 a Class


    (Page 11 of 19 )

    Creating a class using brute-force code generation has similarities with the XSLT approach to code generation. In both cases, you can use regions to organize both your output file and your template, and you can supply a different method to output each region. You start with an entry-level method that’s the rough equivalent to the XSLT entry-level template.

    Creating the Entry-Level Method

    The entry-level method is specified in the Process attribute of the directive in the harness script file. The harness calls this method and expects it to generate a stream containing the output code. The entry-level template has to be a shared (C# static) method. In this case, the entry-level method is GetStream.

    The GetStream method first creates a MemoryStream for output, then an IndentedTextWriter, and some other variables:

    Public Shared Function GetStream( _
       ByVal Name As String, _
       ByVal fileName As String, _
       ByVal genDateTime As String, _
       ByVal nodeSelect As Xml.XmlNode) _
       As IO.Stream
    Dim stream As New IO.MemoryStream
    Dim inwriter As New CodeDom.Compiler.IndentedTextWriter( _
       New IO.StreamWriter(stream))
    Dim nodeList As Xml.XmlNodeList
    Dim nodeColumn As Xml.XmlNode

    GetAttributeOrEmpty is a generic utility method that returns the attribute specified or an empty string if the attribute isn’t present in the XML DOM element. You’ll need a namespace manager when you reference a namespace (via a prefix) in the input XML. The namespace would generally be referenced with a prefix in arguments passed to the SelectSingleNode or SelectNode methods of the XML DOM. A utility method in the Tools class creates the namespace manager:

    Dim singularName As String = Utility.Tools.GetAttributeOrEmpty(nodeSelect, _ "SingularName")

    Dim nsmgr As Xml.XmlNamespaceManager = _ Utility.Tools.BuildNameSpaceManager( _ nodeSelect.OwnerDocument, "dbs", False)

    CAUTION: Namespaces can be hard to handle, especially in .NET, which is unfriendly to the default namespace (as discussed in Appendix A).

    The GetStream method then calls the FileOpen method contained in a separate support file. Placing this method in a central location allows reuse by other brute-force templates. Outputting an empty string using the WriteLine method produces a blank line:

    Support.FileOpen(inwriter, "KADGen,System", fileName, genDateTime)
    inwriter.WriteLine("")

    Another supporting method, WriteLineAndIndent, outputs a line of text and then indents the writer. Changeable information is inserted either as a variable or as a direct lookup of information in the metadata XML. The following outputs the class declaration using a variable (singularName is declared previously):

    Support.WriteLineAndIndent(inwriter, "Public Class " & singularName & _ "Collection")
    inwriter.WriteLine("Inherits CollectionBase")

    Similar to the XSLT code generation, it’s a lot easier to debug if you call a method for each region. The collection class contains two regions so the high-level method calls two other methods to output the core of the class:

    CollectionConstructors(inwriter, nsmgr, nodeSelect)
    PublicAndFriend(inwriter, nsmgr, nodeSelect) Support.WriteLineAndOutdent(inwriter, "End Class")

    The WriteLineAndOutdent method is similar to the WriteLineAndIndent method, and it decreases the indent by one and then outputs the specified text.

    Outputting the row class is much like outputting the collection class. The next code uses a For Each construct to loop through the columns and calls a method for each column:

    inwriter.WriteLine("")
    inwriter.WriteLine("")
    Support.WriteLineAndIndent(inwriter, "Public Class " & singularName) inwriter.WriteLine("Inherits RowBase")
    ClassLevelDeclarations(inwriter, nsmgr, nodeSelect)
    Constructors(inwriter, nsmgr, nodeSelect)
    BaseClassImplementation(inwriter, nsmgr, nodeSelect) Support.MakeRegion(inwriter, "Field access properties")
    nodeList = nodeSelect.SelectNodes("dbs:TableColumns/*", nsmgr)
    For Each nodeColumn In nodeList
      ColumnMethods(inwriter, nsmgr, nodeColumn)
    Next
    Support.EndRegion(inwriter)
      Support.WriteLineAndIndent(inwriter, "End Class")

    Flushing the stream after you’ve finished output ensures that the stream has emptied any buffers and all information is safely in the stream before the writer goes out of scope. Not all writers need to be flushed, but it’s a great habit to be in because it’s essential with many of them:

    inwriter.Flush()
    Return stream
    End Function

    Looking at a Sample Region

    I’ll skip over the Constructors region method because it doesn’t present anything new and instead will walk through one of the more complex region methods. Just like the XSLT templates, the PublicAndFriend region outputs the primary key only if it exists. Passing the namespace manager parameter means you don’t have to re-create it if methods need it for any XML processing:

    Private Shared Sub PublicAndFriend( _
       ByVal inWriter As CodeDom.Compiler.IndentedTextWriter, _
       ByVal nsmgr As Xml.XmlNamespaceManager, _
       ByVal node As Xml.XmlNode)
    Support.MakeRegion(inWriter, _
       "Public and Friend Properties, Methods and Events")

    The SelectSingleNode statement retrieves the primary key element of the TableConstraints element that’s stored in the nodeTemp variable. If this node is found, the value of its Name attribute is assigned to the primaryKeyName variable:

    Dim nodeTemp As Xml.XmlNode = node.SelectSingleNode( _
    "dbs:TableConstraints/dbs:PrimaryKey/dbs:PKField", nsmgr)
    Dim primaryKeyName As String = ""
    Dim primaryKey As Xml.XmlNode
    If Not nodeTemp Is Nothing Then
    primaryKeyName = Utility.Tools.GetAttributeOrEmpty(nodeTemp, "Name")
    End If

    This XPath expression in the next SelectSingleNode uses the primaryKeyName to retrieve the TableColumn node with a Name attribute matching the primaryKeyName:

    primaryKey = node.SelectSingleNode( _
    "dbs:TableColumns/dbs:TableColumn[@Name='" & primaryKeyName & _
    "']", nsmgr)
    Support.WriteLineAndIndent(inWriter, "Public Overloads Sub Fill( _")
    inWriter.Indent += 4

    Once you’ve got the primaryKey node, you can use it in a simple conditional to output a parameter for the primary key value only if there’s a primary key. You can use the Write method to output a partial line without a CRLF:

    If Not primaryKey Is Nothing Then
    inWriter.Write("ByVal " & primaryKeyName & " As ")
    inWriter.WriteLine(Utility.Tools.GetAttributeOrEmpty(primaryKey, _
    "NETType") & ", _")
    End If

    The remainder of the function reuses the same techniques you’ve already seen:

    inWriter.WriteLine("ByVal UserID As Int32)")
    inWriter.Indent -= 4
    inWriter.WriteLine("ByVal UserID As Int32)")
    inWriter.Write(Utility.Tools.GetAttributeOrEmpty(node, "SingularName") & _
    "DataAccessor.Fill(Me")
    If Not primaryKey Is Nothing Then
    inWriter.Write(", " & primaryKeyName)
    End If
    inWriter.WriteLine(", UserID)")
    Support.WriteLineAndOutdent(inWriter, "End Sub")
    Support.EndRegion(inWriter)
    End Sub
     

    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

    - The Transformed XML Explorer in MFC
    - List Control and Property Grid with the MFC ...
    - Font, Shell and Masked Edit Controls for MFC
    - Color, Link and Image Editor Controls for M...
    - New Controls for MFC
    - The Windows Ribbon Framework
    - Markup Language for the Ribbon Framework
    - Visually Upgrade Your MFC Project
    - New Features for the Statusbar in MFC
    - Working with the Statusbar in MFC
    - Iron Speed Design v60 Review
    - Binary and XML Serialization
    - Using CrystalReportViewer to Display Crystal...
    - Creating Summary .Net Crystal Reports
    - More on Commands, Input and the WPF





    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 2 Hosted by Hostway
    Stay green...Green IT