YAMG’�Yet Another Macro Generator

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Environment: .NET

As experienced software developers, we have great disdain for doing mindless work. As previous articles have pointed out, the VS.NET Macro IDE is a terrific means of automating development tasks.

Frequently, both when refactoring code or writing new systems, I need to generate a large number of similar classes, or for some other reason, need to perform a similar change repeatedly. For example, take the following declaration in a VB module:

Private objData as DataSet

Say that you need to make this module-level variable specific on a per-session basis. If you needed to do that, you might replace the variable with the following code:


Private m_objDataCol as Collection
Public Property objData As DataSet
Get
Dim lfoo As Object
If m_objDataCol Is Nothing Then
m_objDataCol = New Collection()
End If
Try
lfoo = m_objDataCol.Item(HttpContext.Current.Session. _
SessionID)
Return m_objDataCol.Item(HttpContext.Current.Session. _
SessionID)
Catch aexc As ArgumentException
m_objDataCol.Add(New DataSet, HttpContext.Current.Session. _
SessionID)
Return m_objDataCol.Item(HttpContext.Current.Session.SessionID)
End Try
End Get
Set(ByVal Value As DataSet)
Dim lfoo As Object
If m_objDataCol Is Nothing Then
m_objDataCol = New Collection()
End If
Try
lfoo = m_objDataCol.Item(HttpContext.Current.Session. _
SessionID)
m_objDataCol.Remove(HttpContext.Current.Session.SessionID)
m_objDataCol.Add(Value, HttpContext.Current.Session. _
SessionID)
Catch aexc As ArgumentException
m_objDataCol.Add(Value, HttpContext.Current.Session. _
SessionID)
End Try
End Set
End Property

The problem is, of course, you end up doing a lot of cut and paste coding in the event that you have many variables to “sessionize” this way (by converting into properties).

For this instance of the issue, the solution is to build a macro that automatically “sessionizes” a module variable. The macro to do this is as follows:


Public Sub MakeGlobalVarSessionVar()

Dim objTD As TextDocument = ActiveDocument.Object
If Split(objTD.Selection.Text, ” “).GetUpperBound(0) >= 3 Then
‘Class name is usually the 4th entry in the selection
‘(good enough for now)

Dim lsCName As String = Split(objTD.Selection.Text, ” “)(3)
‘Variable name is usually the 2nd entry in the selection
‘(again, good enough for now)

Dim lsVName As String = Split(objTD.Selection.Text, ” “)(1)
Dim lsNewCode As String
Dim lsbNewCode As New StringBuilder()
lsbNewCode.Append(” Private m_{V}Col as Collection” & vbCrLf)
lsbNewCode.Append(” Public Property {V} As {C}” & vbCrLf)
lsbNewCode.Append(” Get” & vbCrLf)
lsbNewCode.Append(” Dim lfoo As Object” & vbCrLf)
lsbNewCode.Append(” If m_{V}Col Is Nothing Then” & vbCrLf)
lsbNewCode.Append(” m_{V}Col = New Collection()” &
vbCrLf)
lsbNewCode.Append(” End If” & vbCrLf)
lsbNewCode.Append(” Try” & vbCrLf)
lsbNewCode.Append(” lfoo = m_{V}Col.Item(HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Return m_{V}Col.Item(HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Catch aexc As ArgumentException” &
vbCrLf)
lsbNewCode.Append(” m_{V}Col.Add(New {C}, HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Return m_{V}Col.Item(HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Catch exc As Exception” & vbCrLf)
lsbNewCode.Append(” StoreError(exc)” & vbCrLf)
lsbNewCode.Append(” End Try” & vbCrLf)
lsbNewCode.Append(” End Get” & vbCrLf)
lsbNewCode.Append(” Set(ByVal Value As {C})” & vbCrLf)
lsbNewCode.Append(” Dim lfoo As Object” & vbCrLf)
lsbNewCode.Append(” If m_{V}Col Is Nothing Then” & vbCrLf)
lsbNewCode.Append(” m_{V}Col = New Collection()” &
vbCrLf)
lsbNewCode.Append(” End If” & vbCrLf)
lsbNewCode.Append(” Try” & vbCrLf)
lsbNewCode.Append(” lfoo = m_{V}Col.Item(HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” m_{V}Col.Remove(HttpContext.Current.
Session.SessionID)” & vbCrLf)
lsbNewCode.Append(” m_{V}Col.Add(Value, HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Catch aexc As ArgumentException” &
vbCrLf)
lsbNewCode.Append(” m_{V}Col.Add(Value, HttpContext.
Current.Session.SessionID)” &
vbCrLf)
lsbNewCode.Append(” Catch exc As Exception” & vbCrLf)
lsbNewCode.Append(” StoreError(exc)” & vbCrLf)
lsbNewCode.Append(” End Try” & vbCrLf)
lsbNewCode.Append(” End Set” & vbCrLf)
lsbNewCode.Append(” End Property” & vbCrLf)
lsbNewCode.Replace(“{V}”, lsVName)
lsbNewCode.Replace(“{C}”, lsCName)
objTD.ReplacePattern(objTD.Selection.Text, lsbNewCode.ToString)
End If
End Sub

This macro works by replacing a simple declaration with the more elaborate code requried to “sessionize” the variable using a property.

Whereas this solution is good for this particular case, there are many cases where coding really consists of parameterized replacement or parameterized generation of code. In particular, database-driven classes come to mind (for example, where the classes only differ by name and data fields). For such cases, I have designed a macro generator that writes the boilerplate macro code for code replacement macros:


‘******************************************************************
‘ YAMG() (c) 2003, Aaron Erickson

‘ VS.NET Macro that allows a user to easily write code
‘ generation macros

‘ This Macro creates a new macro template that generates
‘ the code selected in the IDE
‘ It prompts for a Macro name. The user can then add code
‘ to figure out the macro
‘ parameters and code replacement rules.

‘ Use this macro when you encounter a situation where you
‘ have found a prototype class that is expected to spawn
‘ many similar classes
‘ In such a case:
‘ Write and test a class template
‘ Use this macro to generate the macro that writes
‘ a class based on this template
‘ Modify the macro to take in parameters from
‘ a selection, inputbox, or other means
‘ Run the generatedmacro as many times as required
‘ to generate code, or entire classes

‘******************************************************************
Public Sub YAMG()
Dim objMacroDoc As TextDocument =
DTE.MacrosIDE.ActiveDocument.Object
Dim objTD As TextDocument = DTE.ActiveDocument.Object
Dim lsLinesArray() As String =
objTD.Selection.Text.Split(vbCrLf.ToCharArray())
Dim ll As Long
Dim lep As EditPoint
Dim lsb As New StringBuilder()
Dim lsMacroName As String = InputBox(“What should the name
of this macro be?”)
lep = objMacroDoc.StartPoint.CreateEditPoint()
lep.FindPattern(“Public Module”)
lep.LineDown(2)
lsb.Append(” ‘TODO: Automatically Generated Macro from
‘WriteTemplateMacro” & vbCrLf)

lsb.Append(” Public Sub ” & lsMacroName & “()” & vbCrLf)
lsb.Append(” Dim objTD as TextDocument =
ActiveDocument.Object” & vbCrLf)
lsb.Append(” Dim lsbNewCode As New StringBuilder()”
& vbCrLf)
lsb.Append(” ‘TODO: Put in parameter var
‘declarations here” & vbCrLf)

lsb.Append(” ‘For example:” & vbCrLf)
lsb.Append(” ‘Dim lsVName As String =
‘ Split(objTD.Selection.Text, “” “”)
‘ (1)” & vbCrLf)

For ll = 0 To lsLinesArray.GetUpperBound(0) Step 2
lsb.Append(” lsbNewCode.Append(“”” &
lsLinesArray(ll).Replace(“”””,
“”””””) &
“”” & vbCrLf)” & vbCrLf)
Next ll
lsb.Append(” ‘TODO: Put in parameter replacement
‘commands here” & vbCrLf)

lsb.Append(” ‘For example:” & vbCrLf)
lsb.Append(” ‘lsbNewCode.Replace(“”{V}””, lsVName)”
‘& vbCrLf)

lsb.Append(” If objTD.Selection.Text.Length > 0
Then” & vbCrLf)
lsb.Append(” objTD.ReplacePattern(objTD.
Selection.Text,
lsbNewCode.ToString)” &
vbCrLf)
lsb.Append(” Else” & vbCrLf)
lsb.Append(” objTD.Selection.ActivePoint().
CreateEditPoint().
Insert(lsbNewCode.ToString)”
& vbCrLf)
lsb.Append(” End If” & vbCrLf)
lsb.Append(” End Sub” & vbCrLf)
lep.Insert(lsb.ToString())
End Sub

This macro makes it possible to easily write the replacement macro I wrote above, with the only coding task being to, within the generated macro, make changes to determine the macro parameters and apply the appropriate code replacements (comments are generated to help with this).

This macro could easily be extended to be able to generate class generators in VS.NET, by adding code to create and name the new class (easily available within the DTE object model). While I have just discovered this (and in fact, only started playing around with macros in VS.NET yesterday), I am sure there are numerous uses for this type of “macro-macro.”

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read