Skip to content

Code Generation Tutorial

samatstarion edited this page Apr 22, 2025 · 5 revisions

Getting started with Code Generation

This page provides a step-by-step guide on how to create a code-generator in the uml4net solution. The XmiReaderGenerator is used to explain the steps.

  • Code generation starts with normal hand-coding a number of classes to figure out which kind of code needs to be generated and what the best pattern is going to be.
  • Run the ModelInspector to get a list of interesting classes that need to be hand-coded for the pattern. Place these classes in a new folder in the Expected project folder of the uml4net.CodeGenerator.Tests project. Make sure to set the Build Action property of each expected class to None, and the Copy to Output Directory property to Copy Always. In our example the folder is called AutoGenXmiReaders.
  • Add a new .hbs template file to the Templates folder of the uml4net.CodeGenerator project. Set the Copy to Output Directory property to Copy Always. In our example this template is called XmiReaderGenerator.hbs.
  • Add a new Generator class to the Generators folder of the uml4net.CodeGenerator and override the GenerateAsync, RegisterHelpers and RegisterTemplates methods. In our example this class is called XmiReaderGenerator.cs.
  • Read the instructions on handling Primitive Types and how they map to C# types
protected override void RegisterHelpers()
{
    uml4net.HandleBars.StringHelper.RegisterStringHelper(this.Handlebars);
    uml4net.HandleBars.IEnumerableHelper.RegisterEnumerableHelper(this.Handlebars);
    uml4net.HandleBars.ClassHelper.RegisterClassHelper(this.Handlebars);
    uml4net.HandleBars.PropertyHelper.RegisterPropertyHelper(this.Handlebars);
    uml4net.HandleBars.GeneralizationHelper.RegisterGeneralizationHelper(this.Handlebars);
    uml4net.HandleBars.DocumentationHelper.RegisteredDocumentationHelper(this.Handlebars);
    uml4net.HandleBars.EnumHelper.RegisterEnumHelper(this.Handlebars);
    uml4net.HandleBars.DecoratorHelper.RegisterDecoratorHelper(this.Handlebars);
}

protected override void RegisterTemplates()
{
    this.RegisterTemplate("xmi-reader-template");
}
  • Add a new Generator test class to the Generators folder of the uml4net.CodeGenerator.Tests project to execute the generator methods and to verify whether the generated code is equal to the content of the expected classes. In our example this test class is called XmiReaderGeneratorTestFixture.cs. The following code snippet shows a test method that executes for each interesting class. The generated code is compared to the contents of the expected classes. The test will pass when all generated code is equal to the expected code.
[Test]
public async Task Verify_that_concrete_classes_are_generated([Values(
    "Activity","Association",
    "Class", "Connector", 
    "DurationConstraint", "DurationObservation", 
    "ExceptionHandler", "Expression",
    "Generalization",
    "LiteralInteger","LiteralReal","LiteralUnlimitedNatural",
    "OpaqueExpression",
    "Property",
    "StateMachine",
    "TimeConstraint")] string className)
{
    var classes = xmiReaderResult.Root.QueryPackages()
        .SelectMany(x => x.PackagedElement.OfType<IClass>())
        .Where(x => !x.IsAbstract)
        .ToList();

    var @class = classes.Single(x => x.Name == className);

    var generatedCode = await this.xmiReaderGenerator.GenerateXmiReaderAsync(this.xmiReaderDirectoryInfo, @class);

    var expected = await File.ReadAllTextAsync(Path.Combine(TestContext.CurrentContext.TestDirectory, $"Expected/AutoGenXmiReaders/{className}Reader.cs"));

    Assert.That(generatedCode, Is.EqualTo(expected));
}

use an application such as WinMerge to do side by side comparison of expected classes and generated classes.

  • Add content to the handlebars template and start running the generator tests. In case that the generated code is not the same as the expected code, update the template, add helpers etc. untill the tests pass.
  • Once the tests pass, copy the generated code to your destination solution to verify that the code builds and works as expected. In our case, we add the xmireaders (or override the existing readers) to the AutoGenXmiReaders folder of the uml4net.xmi project.

Repeat this process untill you are satisfied with the result.

Clone this wiki locally