Yesterday we finally released VB Migration Partner 1.10 BETA, which registered users can download from our site. Being a beta, we decided to keep the more recent "official" version (1.00.06) online, so that customers can have a choice.

The new version includes so many exciting refactoring features that allow you produce the kind of high-quality VB.NET code that experienced .NET develors can write, only much much faster. For example, you can now reliably merge nested IF blocks or convert On Error Goto and On Error Resume Next statements into Try-Catch blocks by just adding a new project-level pragma.

In this article I show how to leverage the brand-new symbol rename engine. A few customers asked for the ability to rename classes, methods, and controls during the migration process so that the VB.NET code would abide by their naming coding standards. Being the authors of a successful book entirely devoted to coding standard, we couldn't ignore the request.

Earlier versions of VB Migration Partner already included a rename engine - necessary to rename VB6 members that happened to be named after VB.NET keywords, among other things - and more expert users were able to hook into that engine by writing a VB Migration Partner Extender, i.e. a separate DLL that interacts with our tool during the three stages of the migration process (parsing, interpreting, code generation).

DECLARATIVE RULES
Writing an extender requires some familiarity with VB Migration Partner's object model, something most customers don't want toget involved into. (VB Migration Partner already comes with a few extenders that allow you to do complex auxiliary tasks, such as polishing UserControl classes and generating cumulative migration reports when migrating multiple projects in batch mode.)

Thus we decided to expose the inner rename engine to the outside and provide our users with the ability to write declarating rename rules based on regular expressions and stored in an XML file. Thanks to the internal rename engine the entire process was very simple and took a couple of days, most of which were spent to write a series of unit tests and a sample XML file containing a set of standard rename rules for you to use, study, and modify as you see fit.

Enabling this new feature is as simple as adding one ApplyRenameRules pragma anywhere in the VB6 project's source code:

    '## ApplyRenameRules  c:\myrules.xml

The argument is the path of the XML file containing all the rename rules in declarative format. If you omit the argument, the standard RenameRules.xml file that comes with VB Migration Partner 1.10 is used. Instead of adding an ApplyRenameRules pragma in each VB6 project you can store it in a VBMigrationPartner.pragmas file shared by one or more projects. Even better, you can store it in the master pragmas file located in VB Migration Partner's install folder, in which case all yourmigrated projects will use it. Here's a fragment of the XML file that comes with the standard installation:

 <?xml version="1.0" encoding="utf-8" ?>
<NamingRules>
  
   <!-- Components section -->
   <Symbol kind="Component" type="VB.Label" >
      <!-- ensure that we have a "lbl" prefix -->
      <Rule pattern="^(lbl)?" replace="lbl" />
      <!-- drop the "Label" suffix -->
      <Rule pattern="Label$" replace="" />
   </Symbol>

   <!-- more rules for all common controls -->

</NamingRules>

The kind attribute is used first to determine the kind of symbol the rule applies to. Valid names are Form, Class, Module, Type, UserControl, Enum, Component, Method (includes subs and functions), Field, Property, Event, EnumItem (the members of an Enum declaration), Constant, and Variables (includes, local variables and parameters.)

If kind is equal to Component or Variable, you can optionally include a type attribute, that specifies the VB6 type of the member to which the rule applies.In the above example, the type attribue is used to specify that the rule applies to all components of type VB.Label (the standard VB6 label). But you aren't confined to renaming controls: for example, the following rule renames all Integer variables named “i" into “index”

   <Symbol kind="Variable" type="Integer" >
      <Rule pattern="^i$" replace="index" ignoreCase="false" />
   </Symbol>

Renaming rules are based on regular expressions. The pattern attribute is used to decide whether the rule applies to a given member, and the replace attribute specifies how the member should be renamed. The ignoreCase optional attribute should be false if you want the pattern to be applied in case-sensitive mode (by default comparisons are case-insensitive). Finally, the changeCase optional attribute allows you to change the case of the result and can be equal to “lower”, “upper”, “pascal” (first char is uppercase), or “camel” (first char is lowercase).

Being Visual Basic a case-insensitive language, in most cases the default behavior is fine. However, consider the following renaming requirement: many VB6 developers preferred to use “C” as a prefix for all class names, but this guideline has been deprecated in VB.NET. Here’s a rule that drops the leading “C”, but only if it is followed by another uppercase character:

   <Symbol kind="Class" >
      <Rule pattern="^C(?=[A-Z])" replace="" ignoreCase="false" />
   </Symbol>

The changeCase attribute is useful to enforce naming rules that involve the case of identifiers. For example, a few developers like all-uppercase constant names; here's a rule that enforce this guideline:

   <Symbol kind="Constant" >
      <Rule pattern="^.+$" replace="${0}" changeCase="upper" />
   </Symbol>

 

There are a few other options that I don't cover here, if you are interested just have a look at the Renaming Program Members section of our manual. 

PROGRAMMATIC RULES
Declarative rules are easy to write and test - if you are familiar with regexes, at least - but there are times when you want to be in full control of how a symbol or a category of symbols are renamed. Achieving this feat with VB Migration Partner is quite easy:

1) Create a Class Library project inside Visual Studio 2005 or 2008 (you can use any .NET language, including VB.NET or C#)
2) Add a reference to the CodeArchitects.VBMigrationEngine.Dll
3) Create a public class, name it as you prefer, and make it inherit from CodeArchitects.VBMigrationLibrary.SpecialRuleBase
4) Override the GetSymbolNetName method
5) write the code that analyzes the properties of the VBSymbol passed as an argument and return its new name.
6) compile the DLL and drop it in VB Migration Partner's install folder.

For example, let's say that you want to replace the "lng" prefix with the "int" for all 32-bit integer variables defined inside a method named MyMethod. Here's the code that does it:

Public Class MyRenameRules
   Inherits SpecialRuleBase

   Public Overrides Function GetSymbolNetName(ByVal symbol As VBSymbol, _
         ByVal netName As String) As String
      If symbol.Kind = SymbolKind.LocalVar _
            AndAlso symbol.TypeName = "Long" _
            AndAlso symbol.ParentSymbol.Name = "MyMethod" Then
         If netName.StartsWith("lng") Then netName = "int" & netName.Substring(3)
      End If
      Return netName
   End Function
End Class


It couldn't be simpler, uh?

A word of caution: when you supercede VB Migration Partner's rename engine you are responsible for generating names that are both valid and unique. For example, if you drop either a prefix or a suffix, you should ensure that the new name is still unique, otherwise the generated VB.NET project won't compile.