Skip to main content

Extending the UML to C# transformation with Rational Software Architect Version 7 and later

Anil Bhatia (AnilBhatia@in.ibm.com), Senior Staff Software Engineer, IBM
Anil Bhatia is a senior developer with IBM working on Rational UML Modeling tools. His main focus is on Model-Driven Architecture (MDA), specifically for the C# language.

Summary:  The UML to C# transformation capability in IBM® Rational® Software Architect (V7 and later) transforms UML models into C# sources. While the functionality of this transform is complete, you may wish to extend this transformation to include custom elements or conventions. This article introduces the basic elements of creating an extension to this transformation, using a simple example.

Date:  16 Sep 2008
Level:  Intermediate
Activity:  397 views

Introduction

IBM® Rational® Software Architect includes a number of predefined transformations. Some are used to transform a UML model into source code. The UML-to-C# transform, like some of the other model-to-code transformations, can be extended. This article lists the steps necessary to create a simple example extension, one that demonstrates the core capabilities of this functionality.

As with most features in Rational Software Architect, to extend it you use the standard Eclipse extension mechanisms, create a plug-in and implement specific extension points. This article does not address general plug-in development; if you need more information, see the Resources section of this article. Instead, the article covers just those plug-in development skills necessary to create and test a transformation extension, and focuses on customizing the C# transformation.

Reasons that you may need to extend the transformation

Following are several possible reasons that you might need to extend the transformation. You may want to:

  • Add automated documentation to your methods, documentation which lists the parameters and return types.
  • Add author information to each generated type.
  • Change the default method body generated by the transform.

In each case, extending the transform helps you generate your own customized sources.

Getting started

As Figure 1 shows, when the UML to C# transformation is invoked, it begins with the UML model that is configured as the source for the transformation.


Figure 1. Overall transformation structure
UML to C# transformation elements

This transformation operates on the entire model, as shown in Figure 2. The transformation walks through the source, and constructs a C# Abstract Syntax Tree (AST), which is another Eclipse modeling framework (EMF) model. The transformation then used Java<trade/> Emitter Template 2 (JET2) to transform the AST to C# sources. An extension targets at least one part of the overall transformation. A complete list of targets can be found at the end of this article.


Figure 2: Configuring transformation extension elements
UML model

For this simple example, you will define a simple, class-level extension that gets invoked each time a transformation is ready to transform a UML Class element into a C# source file. This extension will do nothing more than look for any keywords defined for this class, and use them to add a new documentation tag associated with the class in the source code.

The overall steps that you will perform are:

  1. Create a new plug-in project for your transformation extension. This project can be combined with other extensions. However, to make the example simple, you will create a new project from scratch.
  2. Add a set of required plug-in dependencies. These plug-ins define the extension point that you are implementing, and provide the APIs that you will need to invoke our extension.
  3. In the plug-in descriptor, define the extension point that identifies the transformation that you want to extend, and the point within it that you want to make a contribution. For more details on the description of an extension point (and how to contribution to it), refer to Notes on the Eclipse Plug-in Architecture in the Resources section.
  4. Provide a Java<trade/> implementation of the Keyword Documentation Rule that will be invoked each time the transformation encounters the element type that you registered an interest in.
  5. Test the transformation by invoking a runtime workbench and executing a UML to C# transformation.

Creating and configuring a new plug-in project

Your transformation extension begins with a new plug-in project.

  1. Select File > New > Project.
  2. Select Plug-in Project from the list of project types, and click Next.
  3. On the first page of the New Plug-in Project wizard, enter a valid project name. The Eclipse convention for plug-ins is to use a URI domain like the one shown in Figure 3.

Figure 3. New Plug-in Project wizard
Project settings and target platform
  1. On the second page (Figure 4) you should update the default Plug-in Name field. Enter Sample UML to CS Extension. The remaining defaults are acceptable.

Figure 4. New Plug-in Project wizard properties page
Plug-in properties and options

On the final page it is not necessary to select a plug-in template, because there are no templates for transformation extensions, only new complete transformations.

  1. Clear the template option and click Finish, as shown in Figure 5.

Figure 5. New Plug-in Project wizard final page
list of available templates

When the wizard finishes, it creates a new plug-in project and opens the Overview page of the project descriptor, as shown in Figure 6.


Figure 6. New project structure
Package Explorer on left, overview on right

The next step is to add a few plug-ins (after switching to the Plug-in Development perspective) as dependencies. These plug-ins contain the extension point definitions to which you are going to provide an implementation, plus APIs that will be used in the implementation of the extension.

  1. Switch to the dependencies tab (found at the bottom of this editor) and add the following plug-ins as a dependency.
    • com.ibm.xtools.modeler
    • com.ibm.xtools.transform.core
    • com.ibm.xtools.transform.uml2.cs
    • com.ibm.xtools.cli.model

The resulting list should look similar to Figure 7.


Figure 7. Dependent plug-ins
tabs along bottom of window

Defining and implementing the extension

The next step is to declare that you are going to provide an implementation to one or more extension points.

  1. Switch to the Extensions tab
  2. Click the Add button to add a new extension. The dialog box shown in Figure 8 displays.
  3. Enter the extension point com.ibm.xtools.transform.core.transformationExtensions.
  4. When it is found, select it and click Finish button.

Figure 8. New Extension dialog
Extension points, wizards, and templates

There is more information that you need to supply in the extension point definition. You can enter these values through the user interface on the page of the Extension tab, but most people find it easier to just edit the raw XML in the plugin.xml file.

This file is created the first time that an extension is defined (before that, it was not necessary, so it was not created by the New Project wizard). Switch to the plugin.xml tab and you will see the XML source, which you can edit.

For this example, add the detail shown in Listing 1 to the extension point.


Listing 1. Extension declaration

   <extension point="com.ibm.xtools.transform.core.transformationExtensions">
      <TransformationExtension
            enabled="true"
            id="com.ibm.rsa.cs.extension.sample.transformationExtensions.id"
            name="My UML to CS Transformation Extension"
            targetTransformation=
                "com.ibm.xtools.transform.uml2.cs.internal.UML2CSTransform"
            version="1.0.0">

         <RuleDefinition
               class="com.ibm.rsa.cs.extension.sample.ClassKeywordDocRule"
               id="com.ibm.rsa.cs.extension.sample.ClassKeywordDocRule.id"
               name="Class Keyword Doc Rule">
         </RuleDefinition>

         <ExtendTransform
               targetTransform="com.ibm.xtools.transform.uml2.cs.internal.ClassTransform">
            <AddRule
                  id="com.ibm.rsa.cs.extension.sample.ClassKeywordDocRule.id">
            </AddRule>
         </ExtendTransform>
      </TransformationExtension>
   </extension>

The body of an extension element is defined by the extension point provider, in this case the Rational Software Architect Core Transformation component (com.ibm.xtools.transform.core.transform). This extension expects TransformationExtension child elements (one for each type of transformation to extend). Each one of these elements defines which transformation is being extended.

  1. For this simple example, specify a targetTransformation with the id com.ibm.xtools.transform.uml2.cs.internal.UML2CSTransform.
  2. You should apply appropriate values for the id, version number, name, and the Boolean value for enabled.

Each transformation extension defines as child elements a rule definition, which specifies a class that implements a rule interface. This class does most of the work of contributing to the transformation, providing you with a place to put your customized processing logic. It is defined as a separate element, because it is possible for a single class to contribute to multiple parts of a transformation (that is, class, property, and operation).

Once all of the rules have been defined, they are matched up with the part of the transformation that they will contribute to with ExtendTransform elements. This element defines the part of the transformation to which a rule will contribute. In other words, once the rules have been created, we need to specify when they should be used.

For this simple example, you want to contribute to the extension each time a new class is being transformed. The value of the targetTransform should be com.ibm.xtools.transform.uml2.cs.internal.ClassTransform. A complete listing of target transform ids can be found in the Appendix section at the end of this article.

Because more than one rule can contribute to any particular part of a transformation, they are collected here as child elements of the ExtendTransform element. You only have one rule defined in your project, so there is a clear and simple mapping. In case of multiple plug-ins (or multiple instances of transformationExtensions, TransformExtension, RuleDefinition, ExtendTransform, or AddRule elements), it would be helpful to make a diagram similar to that shown in Figure 2. The id of the rule in the RuleDefinition element must match that of the AddRule element.

When you have completed your editing, save the file. You should notice a small error icon in the left hand margin next to the line in the plugin.xml file that specifies the implementation class. This indicates that the class could not be found in the project. You can invoke the New Class wizard and use it to create the class file. You can also manually create the class with the File > New menu.

  1. Whichever way you prefer to create Java classes in Eclipse, create one in the designated package, and with the correct name.

You will extend the Rational Software Architect transformation framework class com.ibm.xtools.transform.core.AbstractRule. With this class as a super class, there are only three methods that should be overridden.

  • canAccept: acts like a filter to determine if this rule can accept this source element
  • createTarget: responsible for creating the target element from the source element
  • isSourceConsumed: allow or disable further rules execution on this source element

Each of these methods accepts a transformation context object, which you can query to gain access to the source and target objects. A transformation context object is a collection of various properties related to the transformation, which collection includes overall transformation configuration properties (from Preferences) and properties for the currently executing transform.

With this information, you can decide whether you want to process this particular class (canAccept), or whether further process on this class should stop (isSourceConsumed). If this extension accepts the context, then the createTarget() method is used to make the actual contribution to the transformation. In other words, you use the canAccept() method to further filter when your rule should be executed. The logic for your extension will appear in the createTargetMethod(). You can also decide if you want processing to continue for the element associated with the rule.

For this simple transformation extension, return false in the isSourceConsumed method, to indicate that further processing should continue (as shown in Listing 2). This also indicates that the extension does not mean that all of the work of the transformation of this particular element was completed.

Continuing with the example, indicate in the canAccept() method that you can accept a particular class when the source element is of type UML Class, and there is at least one keyword defined. If there are no keywords, then there is nothing for you to do, so indicate that you are not interested. Access the source UML class through the transformation context that is passed in as an argument.

The getSource() method on the context returns a reference to a UML class, because you specified in the extension point that you were interested only in Class transforms. Type the source object to a UML class, then get the keyword list from it. If the list is empty, then return false. If there are keywords, then you can expect to have the createTarget() method called. This is where you are expected to make your contribution to the transformation.


Listing 2. Java rule class implementation

package com.ibm.rsa.cs.extension.sample;

import java.util.Iterator;

import com.ibm.xtools.cli.model.CompositeTypeDeclaration;
import com.ibm.xtools.transform.core.AbstractRule;
import com.ibm.xtools.transform.core.ITransformContext;
import org.eclipse.uml2.uml.Class;

public class ClassKeywordDocRule extends AbstractRule {

	private static final String NEW_LINE = System.getProperty("line.separator");
	
	public boolean canAccept(ITransformContext context) {
		Object source = context.getSource();
		if (!(source instanceof Class))
			return false;
		
		Class umlClass = (Class)source;
		return (umlClass.getKeywords().size() > 0);
	}
	
	public boolean isSourceConsumed(ITransformContext context) {
		return false;
	}

	protected Object createTarget(ITransformContext context) throws Exception {
		CompositeTypeDeclaration target = 
                            (CompositeTypeDeclaration)context.getTarget();

		if (target == null)
			return null;
		
		String documentation = target.getDocumentation();
		
		Class sourceClass = (Class)context.getSource();
		Iterator keywords = sourceClass.getKeywords().iterator();
		while (keywords.hasNext()) {
			String keyword = (String)keywords.next();
			documentation = " @" + keyword + NEW_LINE + documentation;
		}
		target.setDocumentation(documentation);
		return target;
	}
}

The first step you usually perform when implementing the createTarget() method is to access the source and target objects for this part of the transformation. Because you defined this extension to be invoked when the transformation is processing a UML class, you can safely type the target object to an Abstract Syntax Tree (AST) called CompositeTypeDeclaration object.

The AST is the metamodel for the C# elements. So, a C# program can be represented by an instance of the AST. For example, a class can be represented by an instance of CompositeTypeDeclaration with kind as com.ibm.xtools.cli.model.TypeConstants.CLASS. The com.ibm.xtools.cli.model plug-in provides all of the APIs for manipulating C# source code.

Since your intention is to add the keyword as a tag in the documentation, you have to get the already processed documentation first. Extract the processed documentation from the existing target. Note that if there is any existing C# source code, it is not made available in the target object. The merging of the newly generated code with any existing code happens after all of the model elements have been transformed.

This article will not discuss the AST in any detail here, except to say that when the extension needs to add new elements to the generated source it does so by calling methods on the AST objects. For this example, you want to add a new tag to the class’s documentation. The CompositeTypeDeclaration object stores the documentation as a String. You can modify this documentation as you want, and then set it back.

To determine what keyword to add, access the source object from the transformation context and cast it to a UML Class. From this object, get the list of keywords (similar to how you did so when implementing the canAccept() method). With this list, iterate over the keywords and, for each, create a new @keyword line to be prefixed to the documentation.

The method must return a target object. By default, thisextension Rule gets added as the last rule in the targetTransformation (refer again to the diagram in Figure 2). Therefore, it operates upon the "so far" target of this transformation. Unless the extension rule modifies the return element from the target transform (say an object of a different AST class, or a different object of the same AST class), the extension rule should return the same object that was passed in as a target.

This completes the creation and implementation of our transformation extension.


Testing the extension

Now that the extension is defined and implemented, you need to test it. This is done by creating a debug configuration that opens up a new runtime workbench.

  1. To create this configuration, select Run > Debug from the main menu.
  2. In the dialog, create a new Eclipse Application configuration, and name it appropriately.
  3. You can change the location of the workspace: it is usually a good idea to choose a new location so as to avoid inadvertently affecting an existing workspace.
  4. The remaining defaults are satisfactory (Figure 9).

Figure 9. New Debug configuration
tree view on left, tabs on right
  1. When you finish editing the configuration, press the Debug button to start a new instance of Eclipse.

This new instance will have the transformation extension (which you just authored) installed as a plug-in.

To test the extension, you will need to create a new UML project, and import a Visual Studio C# project into the new instance of the Eclipse shell. You can also import The Visual Studio C# project by clicking the Create New Target Container button while creating a new transformation configuration.

  1. Within the UML model create a test package and class. The resulting workspace should look something like that shown in Figure 10.

Figure 10. Test project in runtime workbench.
palette on right, views on left

Next, you need to add a keyword to the class, because your extension will only work on classes that have a keyword defined.

  1. Select the class (either in the diagram or in the Project Explorer view), and then select the Stereotypes sub-tab in the Properties view.
  2. Enter some text in the Keywords field, as shown in Figure 11.

Figure 11. Setting the keywords property.
diagram on top, tabs and data below

With the test project and model set, you need to create a new transformation configuration that defines the main parameters of the UML to C# transformation.

  1. Select File > New from the main menu.
  2. Then, under the Transformations category, select a new Transformation Configuration and click Next, as shown in Figure 12.

Figure 12. Creating a new transformation configuration
select a wizard from the list

There are many transformation types that are included with Rational Software Architect.

  1. Select the UML to C# transformation, because this is the one to which you created an extension.
  2. Give it an appropriate name, as shown in Figure 13 (this will be used as the filename of the configuration file in the UML project).

Figure 13. Selecting the transformation
list of forward transformations in tree view
  1. The first thing that you need to specify in a transformation configuration is the source model and the target project. Be sure to select the UML model as it appears under the Models folder, and not the actual .emx file.
  2. The target is created by clicking the Create New Target Container button, as shown in Figure 14.

Figure 14. Setting the source and creating the target of the transformation
source on left, target on right

This will launch the.Net Solution Import wizard shown in Figure 15.

  1. Ensure that the .NET Solution is opened in VS 2005, and use the import wizard to import the .NET Solution into Rational Software Architect workspace.

Figure 15. Import .NET solution
Enter the solution file path
  1. Use the Browse button to select the correct Visual Studio C# project to be imported. Once the Visual Studio C# project is imported, it shows up in the target list.
  2. Select the imported C# project as the Selected target for the transform (Figure 16), and click Next.

Figure 16. Select the transformation target
source on left and target on right

You will not get into the details of the other transformation configuration options here.

  1. Click Finish button to create the transformation.

Now that the configuration has been created, it can be invoked. For the life of this project it does not have to be created again.

  1. To invoke the transformation, and your extension to it, right-click the transformation configuration in the Project Explorer view, and select Transform > UML to C# (Figure 17).

Figure 17. Invoking the transformation.
menu command
  1. To examine the results of the transformation, switch to the Visual Studio IDE (Figure 18). You can see how a keyword got added in the class’s documentation.

Figure 18. Results of generated code.
code on left, tree view on right

What you have learned

This simple example of a UML-to-C# extension was meant to demonstrate the basic mechanism for creating extensions. Your extensions would most likely be more intelligent, using information in the model and making more interesting changes to the generated code, or even making additional companion artifacts to the source code.

The important point to take away from this article is that creating transformation extensions to existing transforms makes sense when you want to leverage all of the existing work, and just need to make small changes or contributions to the output. Having one common transformation that generates the code, as well as other artifacts (for instance, deployment descriptors, companion files, and so on) also makes the development process simpler: the developer only has to run one transformation instead of many.


Appendix A. Transform target IDs and source/target types

Depending on what you want to contribute to during the UML to CS Transformation, you will want to contribute to different parts of the existing transform. Each of these parts has a unique ID that must be used as the target transform ID in the plugin.xml file. The following table lists these IDs and the object types that you would expect to see in the transformation configuration.


Model

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
ModelTransform

Source org.eclipse.uml2.uml.Model
Target com.ibm.xtools.cli.model.Project
Target Container Null


Package

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
PackageTransform

Source org.eclipse.uml2.uml.Package
Target null
Target Container null

Class and Structure

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
ClassTransform

Source org.eclipse.uml2.uml.Class or
org.eclipse.uml2.uml.Class with <<CSharp Struct>>
Target com.ibm.xtools.cli.model.CompositeTypeDeclaration with
CompositeTypeDeclaration::kind = com.ibm.xtools.cli.model.TypeConstants.CLASS or
com.ibm.xtools.cli.model.TypeConstants.STRUCT
Target Container com.ibm.xtools.cli.model.CompilationUnit or com.ibm.xtools.cli.model.NamespaceDeclaration or
com.ibm.xtools.cli.model.CompositeTypeDeclaration

Interface

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
InterfaceTransform

Source org.eclipse.uml2.uml.Interface
Target com.ibm.xtools.cli.model.CompositeTypeDeclaration with
CompositeTypeDeclaration::kind = com.ibm.xtools.cli.model.TypeConstants.INTERFACE
Target Container com.ibm.xtools.cli.model.CompilationUnit or com.ibm.xtools.cli.model.NamespaceDeclaration or
com.ibm.xtools.cli.model.CompositeTypeDeclaration

Delegate

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
DelegateTransform

Source org.eclipse.uml2.uml.Class with <<CSharp Delegate>>
Target com.ibm.xtools.cli.model.DelegateDeclaration
Target Container com.ibm.xtools.cli.model.CompilationUnit or com.ibm.xtools.cli.model.NamespaceDeclaration or
com.ibm.xtools.cli.model.CompositeTypeDeclaration

Enumeration Literal

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
EnumerationLiteralTransform

Source org.eclipse.uml2.uml.EnumerationLiteral
Target com.ibm.xtools.cli.model.EnumLiteral
Target Container com.ibm.xtools.cli.model.EnumDeclaration

Generalization

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
GeneralizationTransform

Source org.eclipse.uml2.uml.Generalization
Target null
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration

Interface Realization

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
InterfaceRealizationTransform

Source org.eclipse.uml2.uml.InterfaceRealization
Target null
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration

Property

Target ID
com.ibm.xtools.transform.uml2.java5.internal.
PropertyTransform

Source org.eclipse.uml2.uml.Property with <<CSharp Property>>
Target com.ibm.xtools.cli.model.Property
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration

Operation

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
OperationTransform

Source org.eclipse.uml2.uml.Operation
Target com.ibm.xtools.cli.model.Method or
com.ibm.xtools.cli.model.Operator
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration or
com.ibm.xtools.cli.model.DelegateDeclaration

Field

Target ID
com.ibm.xtools.transform.uml2.java5.internal.
FieldTransform

Source org.eclipse.uml2.uml.Property or
org.eclipse.uml2.uml.Property with <<CSharp Field>>
Target com.ibm.xtools.cli.model.Field
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration

Parameter

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
ParameterTransform

Source org.eclipse.uml2.uml.Parameter
Target null
Target Container com.ibm.xtools.cli.model.Method

Event

Target id
com.ibm.xtools.transform.uml2.cs.internal.
EventTransform

Source org.eclipse.uml2.uml.Parameter with <<CSharp Event>>
Target com.ibm.xtools.cli.model.Event
Target Container com.ibm.xtools.cli.model. CompositeTypeDeclaration

Indexer

Target ID
com.ibm.xtools.transform.uml2.cs.internal.
IndexerTransform

Source org.eclipse.uml2.uml.Operation with <<CSharp Indexer>>
Target com.ibm.xtools.cli.model.Indexer
Target Container com.ibm.xtools.cli.model. CompositeTypeDeclaration

Realization

Target ID
com.ibm.xtools.transform.uml2.cs.internal. RealizationTransform

Source org.eclipse.uml2.uml.Realization
Target null
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration

PartialDependency

Target ID
com.ibm.xtools.transform.uml2.cs.internal. PartialDependencyTransform

Source org.eclipse.uml2.uml.Dependency with <<CSharp Partial>>
Target null
Target Container com.ibm.xtools.cli.model.CompositeTypeDeclaration


Resources

Learn

Get products and technologies

Discuss

About the author

Anil Bhatia is a senior developer with IBM working on Rational UML Modeling tools. His main focus is on Model-Driven Architecture (MDA), specifically for the C# language.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational, Sample IT projects
ArticleID=337068
ArticleTitle=Extending the UML to C# transformation with Rational Software Architect Version 7 and later
publish-date=09162008
author1-email=AnilBhatia@in.ibm.com
author1-email-cc=clarkega@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers