# Friday, November 24, 2006

This was meant as a "Part II" to my prior post on generating Text, HTML & more XML with XSLT.  The point for today's post is that source code files are text files.  The example I am thinking of is generating a library of VB.NET wrapper classes for the stored procedures in a SQL Server database.

(I've also ticked the Continuous Integration category for this post.  It wouldn't be hard to think of a scenario where a build process would generate a library from a reference database on the check-in of a stored proc script, then deliver the latest rev of the library to the developers, anyway...)

Step one would be fetch the meta data about the stored procedures, for example: 

SELECT procs.Specific_Name, params.Parameter_Name, params.Data_Type, params.Parameter_Mode
FROM
INFORMATION_SCHEMA.ROUTINES procs
LEFT
JOIN INFORMATION_SCHEMA.PARAMETERS params
ON params.Specific_Name = procs.Specific_Name
WHERE
procs.Routine_Type = 'PROCEDURE' AND
procs
.Specific_Name NOT LIKE 'dt_%'
FOR
XML AUTO

Once we add a document node to this, we will have a document that contains many element sets like this one:

  <procs Specific_Name="GetContactByID">
    <params Parameter_Name="@ContactID" Data_Type="nvarchar" Parameter_Mode="IN"/>
    <params Parameter_Name="@ContactGUID" Data_Type="int" Parameter_Mode="INOUT"/>
    <params Parameter_Name="@Found" Data_Type="bit" Parameter_Mode="INOUT"/>
  </procs>

I think that's all we need to get started.

Step two is to transform this data into VB.NET code.  For today's example it suits me to generate two files: 

  1. One will contain classes that wrap ADO.NET calls to the database
  2. The other will provide types that wrap properties for passing into and out of the first

The text of the first template is as follows

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="
http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" indent="yes" />
 
  <xsl:template match="/">

Imports System
Imports System.Data.SqlClient
Imports System.Data
Public Class DatabaseAccess
    Inherits ConvienientBaseClass
    Public Sub New(ByVal cn As SqlConnection, ByVal trn As SqlTransaction)
        MyBase.New(cn, trn)
    End Sub
   
    <xsl:apply-templates select="/database/procs" />
    
End Class
  </xsl:template>
 
  <xsl:template match="procs">
    '
    ' Wraps stored proc: <xsl:value-of select="@Specific_Name" />
    '
    Public Function Execute<xsl:value-of select="@Specific_Name" />(ByVal params As <xsl:value-of select="@Specific_Name" />Struct) _
                                             As <xsl:value-of select="@Specific_Name" />Struct
        Dim exec As New SqlCommand
        Dim param As SqlParameter
        With exec
            .CommandText =
"<xsl:value-of select="@Specific_Name" />"
            .CommandType = CommandType.StoredProcedure
            .Connection = MyBase.DatabaseConnection
            .Transaction = MyBase.CurrentTransaction
        End With
  <xsl:apply-templates select="params" /> 
        Try
            If Not exec.Connection.State = ConnectionState.Open Then exec.Connection.Open()
            exec.ExecuteNonQuery()
        Catch ex As Exception
            Throw
        Finally
            If Not exec Is Nothing Then exec.Dispose()
        End Try
    End Function
  </xsl:template>
 
 
  <xsl:template match="params">
        param = exec.CreateParameter
        With param
            <xsl:if test="@Parameter_Mode='INOUT'">.Direction = ParameterDirection.Output</xsl:if>
            <xsl:if test="@Parameter_Mode='IN'">.Direction = ParameterDirection.Input</xsl:if> 
            .DbType = DbType.<xsl:value-of select="@Data_Type" />
            .Value = params.<xsl:value-of select="@Parameter_Name" />
            .ParameterName =
"<xsl:value-of select="@Parameter_Name" />"
        End With
        exec.Parameters.Add(param)
 
</xsl:template>
 
</xsl:stylesheet>

And the text of the second template is as follows:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="
http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" indent="yes" />
 
  <xsl:template match="/">

Imports System
    <xsl:apply-templates select="/database/procs" /> 
  </xsl:template>

 
  <xsl:template match="procs">
'
' Parameters for method: Execute<xsl:value-of select="@Specific_Name" />
'
Public Class <xsl:value-of select="@Specific_Name" />Struct
   <xsl:apply-templates select="params" />  
End Class 
  </xsl:template>
 
  <xsl:template match="params">
   Private _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" /> As Date
 Public Property <xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" />() _
                                                                     As  <xsl:value-of select="@Data_Type" />
    Get
      Return _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" />
    End Get
    Set(ByVal Value As <xsl:value-of select="@Data_Type" />)
      _<xsl:value-of select="substring(@Parameter_Name,2,string-length(@Parameter_Name)-1)" /> = Value
    End Set
   End Property
 
</xsl:template>
 
</xsl:stylesheet>

To my eye, these look more like VB.NET source files than XSLT templates.  That's because they started life as .vb files.  Then I renamed them .xslt and started inserting the XSLT tags in places where I needed substitution from the XML source. 

To emphasise the hybrid-ness (is that a word) of these files, I have highlighted the VB.NET parts blue and the XSLT parts green, rather than keeping the VB.NET syntax highlighting.

Some breif thoughts: 

  • The value of this to my mind is in not having to hand code *every* one.  You hand code one, then generate the rest - in theory this reduces the opertunity for bugs which should be the focus of the exercise. 
  • Maybe this may find a home in a long-running project to have this as part of the build process, or maybe as part of some tooling focused on small one-off
  • This are commercial products that do code gen based on XSLT.  I haven't used them, but they may well be better than my home-brew sample :-)
  • Maybe useful for trainers, or producting samples for demos etc.  I'm thinking now about times where there may be a need to generate side-by-side VB.Net, C# & Java sample code for example.
  • Also include comment blocks that are readable by NDoc!

[edit: added line breaks for formatting.]

Friday, November 24, 2006 10:58:13 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, November 22, 2006

When I need to make a skype call, I

  • Set my status on MSNWindows Live Messenger to (In a call)
  • Set my Skype status to DND
  • Set my desk phone to DND
  • Maybe, log it in my time tracking system (Excel)
  • Pause iTunes
  • Say "hang on a min mate" in all my active MSNWindows Live Messenger & Skype Chat windows

...then I can make a call.  Why can't this stuff all just work together.

When I lock my PC (as I do when ever I get out of the my chair) I need to:

  • Pause iTunes
  • Set my Skype status to Away
  • Set my MSN status to Away
  • Set my Desk Phone to DND
  • Say "be right back" in all my active MSNWindows Live Messenger & Skype Chat windows

Guess what?!  Not all of that happens every time :-(

I know it *can* play well together, like MSNWindows Messenger knows what song I am listening to in iTunes because it is visible in my status! 

Wednesday, November 22, 2006 10:22:28 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [2]  | 
# Wednesday, November 15, 2006

I just thought it would be more sense to provide a link to the files mentioned in my last post.  Grab them if you are interested.

Wednesday, November 15, 2006 9:45:46 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, November 13, 2006

I've hacked on a little bit about XSLT and I think it's about time I added some more.  So this post will serve as the next instalment, and somewhat of an introduction to the uninitiated.

Lets say we were to consider the following data:

Position Horse Jockey
1 Delta Blues (JPN) Y. Iwata
2 Pop Rock (JPN) D. Oliver
3 Maybe Better (AUS) C. Munce
4 Zipping (AUS) G. Boss
5 Land 'n Stars (GB) J. Egan
6 Mahtoum (AUS) C. Brown
7 Yeats (IRE) K. Fallon
8 Activation (NZ) M. Rodd
9 Mandela (NZ) C. Williams
10 Glistening (GB) S. Seamer

As XML, it could concievably arrive like this:

<raceResults>
<finish result="1" jockey="Y. Iwata" originISO="JPN">Delta Blues</finish>
<finish result="2" jockey="D. Oliver" originISO="JPN">Pop Rock</finish>
<finish result="3" jockey="C. Munce" originISO="AUS">Maybe Better</finish>
<finish result="4" jockey="G. Boss" originISO="AUS">Zipping</finish>
<finish result="5" jockey="J. Egan" originISO="GB">Land 'n Stars</finish>
<finish result="6" jockey="C. Brown" originISO="AUS">Mahtoum</finish>
<finish result="7" jockey="K. Fallon" originISO="IRE">Yeats</finish>
<finish result="8" jockey="M. Rodd" originISO="NZ">Activation</finish>
<finish result="9" jockey="C. Williams" originISO="NZ">Mandela</finish>
<finish result="10" jockey="S. Seamer" originISO="GB">Glistening</finish>
</raceResults>

And that is fine, and may well even be useful.  The place where we should introduce XSLT is when we would like to do something with it like turning that XML into text, HTML, or more XML.

Text is an easy one to start with.  Consider the following stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>

<xsl:template match="/">
   <xsl:apply-templates select="raceResults"/>
</xsl:template>

<xsl:template match="raceResults">
position,jockey,horse,
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
   <xsl:value-of select="@result"/>,<xsl:value-of select="@jockey"/>,<xsl:value-of select="."/>,
</xsl:template>

</xsl:stylesheet>

When applied to our XML, it will produce the following result:

position,jockey,horse,
1,Y. Iwata,Delta Blues,
2,D. Oliver,Pop Rock,
3,C. Munce,Maybe Better,
4,G. Boss,Zipping,
5,J. Egan,Land 'n Stars,
6,C. Brown,Mahtoum,
7,K. Fallon,Yeats,
8,M. Rodd,Activation,
9,C. Williams,Mandela,
10,S. Seamer,Glistening,

The trick is first to not look for flow control in the traditional fashion (For loops, etc).  Instead the XSLT processor will treat it in the following manner:

  1. Start at the top of the source document - denoted with a /
  2. Every time you find a raceResults do what is in the raceResults template.
  3. (now inside the raceResults template) output the literal string: position,jockey,horse,
  4. (still inside the raceResults template) every time you find a finish, do what is in the finish template
  5. (now inside the finish template) Pick out the attribute called result, then pick out the attribute called jockey, then pick out what ever is contained in this element.  NB:  Attributes are denoted by the @

And that is it!

Now did you notice how literal text was handled?  All the bits of your output can live inside the template.  Now if we wanted an HTML representation we would use the following template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html"/>

<xsl:template match="/">
   <html><head><title>Race Results</title></head><body>
   <xsl:apply-templates select="raceResults"/>
   </body></html>
</xsl:template>

<xsl:template match="raceResults">
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
   <p>Position <b><xsl:value-of select="@result"/></b>was <b><xsl:value-of select="."/></b>ridden by <b><xsl:value-of select="@jockey"/></b></p>
</xsl:template>

</xsl:stylesheet>

...and following exactly the same steps as the text example, the XSLT processor will output the following HTML:

Position 1 was Delta Blues ridden by Y. Iwata

Position 2 was Pop Rock ridden by D. Oliver

Position 3 was Maybe Better ridden by C. Munce

Position 4 was Zipping ridden by G. Boss

Position 5 was Land 'n Stars ridden by J. Egan

Position 6 was Mahtoum ridden by C. Brown

Position 7 was Yeats ridden by K. Fallon

Position 8 was Activation ridden by M. Rodd

Position 9 was Mandela ridden by C. Williams

Position 10 was Glistening ridden by S. Seamer

As you can imagine, it can be easy to get a stylesheet for HTML output that contains a lot of HTML markup!  The important thing to note in this example is that the HTML inside the stylesheet must be valid XML as well as valid HTML.  Note the closing </p> which would normally be optional in HTML is manditory here.

Now we get to the fun example:  Turning XML into XML :D

Why would we want to turn XML into XML?  For as many reasons as you have XML in your life!  Imagine turning the above document into its own RSS feed for example.

We could use the following template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">

<rss version="2.0">
   <channel>
      <title>Race News</title>
      <link>http://www.horses.com/</link>
      <description>Live race results as they cross the finish line.</description>
      <language>en-us</language>
      <pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
      <lastBuildDate>Tue, 13 Nov 2006 17:45:00 AEST</lastBuildDate>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs>
      <generator>My RSS transformation</generator>
      <managingEditor>james@deepdark.net</managingEditor>
      <webMaster>james@deepdark.net</webMaster>
     
   <xsl:apply-templates select="raceResults"/>
  
   </channel>
</rss>

</xsl:template>

<xsl:template match="raceResults">
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
      <item>
         <title><xsl:value-of select="."/></title>
         <link>http://www.horses.com/lookup?<xsl:value-of select="."/></link>
         <description>Registered in: <xsl:value-of select="@originISO"/> .  Ridden by: <xsl:value-of select="@jockey"/></description>
         <pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
         <guid>http://www.horses.com/results/race6273-<xsl:value-of select="@result"/></guid>
      </item>
</xsl:template>
</xsl:stylesheet>

This example is the reason I took you so far down the page.  It builds on the key points of the prior two examples: 

  1. The output can mingle in stylesheet, so long as it is valid XML.  I hope you have noticed that the stylesheet itself is valid XML too.
  2. Again there are no flow control pieces.  In fact is is using exactly the same 5 step process I outlined for the first example (you guessed it, this was no mistake :)

The output is the following lovely RSS feed:

<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Race News</title>
<link>http://www.horses.com/</link>
<description>Live race results as they cross the finish line.</description>
<language>en-us</language>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<lastBuildDate>Tue, 13 Nov 2006 17:45:00 AEST</lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>My RSS transformation</generator>
<managingEditor>james@deepdark.net</managingEditor>
<webMaster>james@deepdark.net</webMaster>
<item>
<title>Delta Blues</title>
<link>http://www.horses.com/lookup?Delta Blues</link>
<description>Registered in: JPN .  Ridden by: Y. Iwata</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-1</guid>
</item>
<item>
<title>Pop Rock</title>
<link>http://www.horses.com/lookup?Pop Rock</link>
<description>Registered in: JPN .  Ridden by: D. Oliver</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-2</guid>
</item>
<item>
<title>Maybe Better</title>
<link>http://www.horses.com/lookup?Maybe Better</link>
<description>Registered in: AUS .  Ridden by: C. Munce</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-3</guid>
</item>
<item>
<title>Zipping</title>
<link>http://www.horses.com/lookup?Zipping</link>
<description>Registered in: AUS .  Ridden by: G. Boss</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-4</guid>
</item>
<item>
<title>Land 'n Stars</title>
<link>http://www.horses.com/lookup?Land 'n Stars</link>
<description>Registered in: GB .  Ridden by: J. Egan</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-5</guid>
</item>
<item>
<title>Mahtoum</title>
<link>http://www.horses.com/lookup?Mahtoum</link>
<description>Registered in: AUS .  Ridden by: C. Brown</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-6</guid>
</item>
<item>
<title>Yeats</title>
<link>http://www.horses.com/lookup?Yeats</link>
<description>Registered in: IRE .  Ridden by: K. Fallon</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-7</guid>
</item>
<item>
<title>Activation</title>
<link>http://www.horses.com/lookup?Activation</link>
<description>Registered in: NZ .  Ridden by: M. Rodd</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-8</guid>
</item>
<item>
<title>Mandela</title>
<link>http://www.horses.com/lookup?Mandela</link>
<description>Registered in: NZ .  Ridden by: C. Williams</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-9</guid>
</item>
<item>
<title>Glistening</title>
<link>http://www.horses.com/lookup?Glistening</link>
<description>Registered in: GB .  Ridden by: S. Seamer</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-10</guid>
</item>
</channel>
</rss>

I think this technique is going to be ever more important in the future.  Take in some SOAP, spit out some HTML.  Take in some XML from one API, spit it out as RSS.  A great tool for the toolbox.

Monday, November 13, 2006 7:22:02 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, November 07, 2006
So here is what I want from the perfect laptop.
  • 15 inch widescreen capable of 1600x1000 - good for coding.
  • Weigh no more than 2Kg
  • All the standard bits like WiFi, a Windows key & better support for Suspend than my aging Inspiron 1100.  I wanna be like a mac guy and just shut the lid of the machine and not worry about it.  With my Inspiron that would cause a fire.
  • 100Gb SATA HDD, 2Gb RAM, Dual Core, Vista Ready
  • Definately less than AUD$3000, closer to $2000 the better
Here's the kicker:  Convertable tablet form factor and it kicks the res down to 1125x864, or 1024x768 when flipped for tablet-like note taking and ad-hoc jotting.

One day I'm gonna be looking back on this post on such a machine and grin :)

Tuesday, November 07, 2006 6:41:08 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, November 06, 2006
Its been a little while since my last Language Nerd post, so...

Rory Blyth has had a seriously cool and often unhinged blog for quite some time now.  He's tapping into the wealth of his best blog posts for his new podcast, The Smartest Man In the World

He really is an imaginative and original writer.  Go check it out ;)

Monday, November 06, 2006 1:53:11 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
Monday, November 06, 2006 1:34:22 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, November 01, 2006
I love it when things come together:
CodeRush and Refactor! - I'm a fan
I love a good keyboard too.

Here's a nice little plugin for Refactor! that can show available refactorings or metrics on the display of a Logitech G15.  Too cool!

Wednesday, November 01, 2006 6:33:20 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
I thought it was about time I refreshed my rules for what good error messages are:

  • Consistant.  This could be visually consistant in the case of message boxes, or if they are being logged to a file or database table then being consistant helps them also to be machine readable.
  • Informative.  Consider ADO & ADO.Net's old "General Network Error" as the best example of how not to do errors.  The key point here is the message should first say:
  1. what did not work correctly, and
  2. what you should do next to fix it
Stack traces etc should come later.
  • Appropriate.  Consider the audience of your application.  They are the audience for your erorrs too!  The level of detail I expect in an error from SQL Server is completely different to the kind error I expect from iTunes. 
  • Respect my attention span.  750 Event Log messages a day means I won't look at them.  Once a quater means I won't look at them.  The latter case should be emailed.  The former case probabbly should be replaced with one critical failure message :-)
  • Respect my privacy.  Passwords, maybe even user names, or anything that is sensative in the context of the application should not apear in an error message.  I don't have a convienient example of one with a password in it (maybe check a more complete source) but this would probabbly also extend to stack traces.
This is a growing list.  It gets revised when I see something done well, or something done poorly (or when I do something well, or when I do something poorly.  Now I've blogged it.

It started out with just the Informative rule, motivated by the cost of the support desk having to get involved in a support issue vs. the user working it out for themselves.

More formally, we could look at an error with the following criteria:
  • What does this message do for the user experience?
  • What does this message do to avoid the user having to call someone?


Wednesday, November 01, 2006 6:22:55 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [1]  | 
# Monday, October 30, 2006
Monday, October 30, 2006 10:17:15 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  |