Figure 1 - Sample WithClass Diagram for Generating C#
Introduction
WithClass is a UML tool that allows users to draw UML diagrams, reverse engineer code (Java, C#, PHP) and even generate code. In the old days, the way UML tools generated code was either from some proprietary scripting language or by using VBA through COM. In recent years some cool solutions for templating code have arrived on the scene. First came Velocity for java which uses something called VTL (Velocity Template Language) for generating code. Then Castle decided to write a subset of Velocity for .NET and called it NVelocity. WithClass works with NVelocity and also has built in C# Scripting to allow you to talk directly to NVelocity via the WithClass UI. In this article, we'll delve into how you can use WithClass to generate C# Code using these two new features.
Running NVelocity
If you go into the File menu in WithClass and choose Edit C# Script File, It will bring up an editor that will allow you to pick a C# script to run for generating C# classes. WithClass comes with a few NVelocity C# Scripts integrated into the tool to get you started, but you can just as easily create your own custom scripts. For our example we'll choose the script NVelocityClasses.cs in the WithClass script directory and this will bring up the script shown in listing 1.
Listing 1 - Script for generating C# Code in WithClass using the NVelocity Engine
using NVelocity;
using Commons;
using WithClass;
using System.IO;
using System.Windows.Forms;
using Velocity = NVelocity.App.Velocity;
using VelocityEngine = NVelocity.App.VelocityEngine;
using VelocityContext = NVelocity.VelocityContext;
using Template = NVelocity.Template;
using ParseErrorException = NVelocity.Exception.ParseErrorException;
using ResourceNotFoundException = NVelocity.Exception.ResourceNotFoundException;
using System.Collections.Generic;
/// <summary>
/// This class is a simple demonstration of how the NVelocity Template Engine
/// can be used inside of WithClass.
/// </summary>
public static void Test() {
StreamWriter sw = null;
try {
VelocityEngine fileEngine = new VelocityEngine();
fileEngine.Init();
// Make a context object and populate with the data. This
// is where the Velocity engine gets the data to resolve the
// references (ex. $list) in the template
VelocityContext context = new VelocityContext();
// put the states and the transitions into the context
context.Put("classes", Globals.ActiveDocument.Classes);
// set the output file
sw = new StreamWriter(@"c:\test4.txt");
string path = @"#ScriptPath#\classes.vm";
StreamReader sr = new StreamReader(path);
string templateString = sr.ReadToEnd();
sr.Close();
fileEngine.Evaluate(context, sw, null, templateString);
sw.Close();
} catch (System.Exception e) {
sw.Close();
MessageBox.Show(e.Message);
}
}
You can pretty much write any C# code you want inside of WithClass or use any of the built in .NET framework elements, so if you are familiar with the .NET, programming scripts inside of WithClass is fairly simple. The first part of listing 1, initializes the VelocityEngine. This is necessary if you want velocity to work correctly. The next part of the code sets up the context of your script. The context is the items you are planning on injecting into the template you provide. For our WithClass script, we will inject classes that are drawn inside the WithClass UI. Currently WithClass allows you to inject Classes, States, and Transitions. In the future, other diagrams will also be supported.
The NVelocity Template
NVelocity templates have the extension .vm and contain the VTL necessary to generate populated templates from the supplied context. Listing 2 shows the template. All keywords that are part of VTL are highlighted in blue. All of the variables in the VTL are highlighted in brown.
Listing 2 - classes.vm NVelocity VTL Template File
# This is an example velocity template
#set( $this = "WithClass")
// $this generated this code
#foreach( $class in $classes )
#if ($class.Namespace != "")
namespace $class.Namespace
{
#end
#if ($class.BaseClasses.Count() > 0)
$class.Visibility class $class.Name : #foreach ($base in $class.BaseClasses) $base.Name #end
#else
$class.Visibility class $class.Name
#end
{
#foreach ($attribute in $class.Attributes)
$attribute.Visibility $attribute.Type $attribute.Name;
#end
#foreach ($operation in $class.Operations)
$operation.Visibility $operation.ReturnType $operation.Name ( #foreach ($parm in $operation.Parameters) $parm.Type $parm.Name
#between
, #end )
{
$operation.Code
}
#end
}
#if ($class.Namespace != "")
}
#end
#end
VTL is a language in that it has loops, conditions, and even variables. However, it is also a template because you can write any string in your file, and it will get rendered the way you write it. Let's examine some of the constructs in the VTL in listing 2. Note that all command keywords in VTL are prefaced by a #. All variables are prefaced by a $. There are 3 main control keyword groups used in the template script in listing 2: #set, #foreach #end, and #if #else #end. These control keywords are similar to keywords in other languages. The #set is used for assignment to a variable. The #if #else #end is used as a conditional, and #foreach #end loop through an array in your context. The first and only #set in our VTL script sets the $this variable to "WithClass". The line shown below
#foreach( $class in $classes )
loops through each class in the WithClass classes context (assigned in listing 1), and pulls out a $class variable. The $class variable can be used to access properties of a WithClass class in the class diagram. A class variable contains properties such as Name. The class variable also contains Attribute and Operation lists. You can loop through these contained lists again using #foreach #end.
First we loop through each attribute in listing 2 and form a line of C# Code from the internal data in our diagram in WithClass.
#foreach ($attribute in $class.Attributes)
$attribute.Visibility $attribute.Type $attribute.Name;
#end
The visibility, type, and name of the attribute will be written out in the template just as it is shown in the template. For example, the Media class in figure 1 contains a private attribute _serialNumber with type string. NVelocity will generate the following line from the VTL above.
private string _serialNumber;
For the operation #foreach loop, you'll notice another internal #foreach loop for parameters. The parameters loop has a special keyword #between inside of it. Although I'm not sure this is standard VTL, what it does in NVelocity is it tells the loop to only place the text that follows between all of the parameters. This way you don't get a trailing comma on the end of your parameter list.
#foreach ($operation in $class.Operations)
$operation.Visibility $operation.ReturnType $operation.Name ( #foreach ($parm in $operation.Parameters) $parm.Type $parm.Name
#between
, #end )
{
$operation.Code
}
#end
}
If you are curious about some of these fancy #foreach keywords, check out the castle project site improvements page.
Generating Code
You can run the script right inside of WithClass by going to the Tools menu in the C# Script editor. After clicking Run Script..., WithClass will read in the NVelocity template classes.vm and evaluate it against the context supplied by the NVelocityClasses.cs script.
Figure 2 - Running the NVelocity Script from Inside WithClass
In the case of the script in listing 1, we placed our output directly in the root of the C drive in a file called test4.txt. You can easily modify the script to place the code where you want. The results of running the script on the WithClass class diagram in figure 1 are shown in listing 3:
Listing 3 - Results of running NVelocity C# Template Script from WithClass
// WithClass generated this code
namespace Sample.IO
{
public class File : Media
{
private byte[] _buffer;
public string Read( )
{
return System.Text.ASCIIEncoding.ASCII.GetString(_buffer);
}
public void Write(String content)
{
_buffer = System.Text.Encoding.UTF8.GetBytes(content);
}
public File(int length)
{
_buffer = new byte[length];
}
}
}
namespace Sample.IO
{
public class Media
{
private string _serialNumber;
}
}
Conclusion
NVelocity provides us with a powerful and easy way to generate code from a file template. If you need to do code generation, a UML tool such as WithClass provides you with an easy way to leverage the power of NVelocity to generated code. Although are example showed you how you could generate C# code with WithClass, you could just have easily modified the same NVelocity script for generating Java, Ruby, PHP, Python, Objective C, or even Object Cobol. In our next article, we'll show you how you can leverage NVelocity with State Diagrams to generate a state engine in C#. If you are interested in experimenting with NVelocity and WithClass, you can download the trial version of WithClass and start generating code right away. WithClass currently sells for $99 and can be purchased on the Microgold webstite