Extending the UML to C++ transformation by using IBM Rational Software Architect 7.0

How to extend the transformation, step by step


Common reasons for extending the transformation

There are many reasons that you may have for wanting to extend the transformation. For instance, you may want to add automated documentation to your methods, that lists the parameters and return types. Or you may want to change the default method body generated by the transformation (transform, for short). In each case, extending the transform helps you generate your own, customized sources.

How the process differs from Version 6

The UML to C++ transformation process has been completely redesigned in Version 7.0. Before, in Version 6, the transform walked through a UML model and wrote directly into an output buffer that eventually dumped its contents as C++ files. Thus you needed to modify what was written into the buffer.

In Version 7.0, the transform walks through a UML model and constructs a C++ abstract syntax tree, which is nothing but an EMF model. The transform then uses JET2 to transform the model into C++ sources. You need to tap into this tree to modify the generated code in any way. Thus in this case, you need to have some knowledge of this intermediate model -- especially some of the central classes that contribute to code generation for C++, such as class, struct, union, enum, method, and attribute.

The model classes belong to a plug-in called The Javadoc™ tool for the model is attached as an appendix to the document, as the diagram in Figure 1 illustrates.

Figure 1. The UML to C++ transformation
2 diagrams and code
2 diagrams and code

Examples included with this article

The examples available with this article in a separate file (see Download) take you through the basics of extending the C++ transformation in Rational Software Architect v7. If you have it installed, then simply extract the examples in your plug-ins directory, restart Rational Software Architect, and run these transformations:

  • Example 1 changes the default method body to something that you would want.
  • Example 2 adds a comment before each method.
  • Example 3 adds a comment before each attribute.

This article steps you through Example 1 only. Examples 2 and 3 are similar, and you can explore them on your own.

Overview of using extension points

The UML to C++ transformation can be extended through the extension point. This extension point enables you to add rules, content extractors, or transformations to an existing transformation, such as the UML to C++ transformations. For an extension, the following items can be defined:

  • Property: New properties for the transformation
  • RuleDefinition: New rules to extend the current behavior
  • ExtractorDefinition: New content extractors for walking the model
  • TransformDefinition: New transforms (containers for extractors and rules)
  • ExtendTransform: Add new rules, extractors, or transforms to an existing transform

This article concentrates on extending the UML to C++ transformation by adding new rules, because that is what you will most commonly need to do.

How to add a new rule to the C++ transformation

There are three things that you need to do to add a new rule to the transform:

  1. Define the rule by using RuleDefinition.
  2. Extend the transform to insert the new rule by using ExtendTransform.
  3. Define the class that represents the rule.

Figure 2 is a snapshot of how your XML plug-in will look after you have added a rule to override the default method bodies in the generated code. Notice this line, in particular: (AddRule)
Figure 2. XML plug-in after adding a rule
XML code screen capture
XML code screen capture

1. Define the new rule

Listing 1 shows what a RuleDefinition looks like.

Listing 1. Sample RuleDefinition
       description="Adds a user defined body to a method" 
       name="C++ Method Body Rule"/>

As you might have guessed, the class name refers to the actual class that defines the rule. The ID attribute uniquely identifies the rule to the transformation framework. The name and description attributes are just that: a name of your choosing for the rule and a description of what it does.

2. Insert the new rule

After you have defined a new rule, you need to insert it at the appropriate place in the appropriate transform, and this is what you do by using ExtendTransform.method shown in Listing 2. The appropriate transform part is, of course, easy to understand. The appropriate place needs some explanation, though, so we'll get to that shortly.

Listing 2. ExtendTransform example
       <ExtendTransform targetTransform=

In this example, the method body rule needs to go into an Operation transform. We've selected the ClassOperationTransform, which means that this new rule will affect only the behavior of operations defined in UML classes or stereotyped UML classes and not, for example, operations defined in UML interfaces. Notice that the ID of the rule corresponds to the ID that you used when you defined your new rule by using RuleDefinition.

The next important thing to notice is the index, which determines where your rule is going to be linked in the transformation chain that you're inserting it into. This is extremely important, because it determines what kind of processing has already been done on the element that you're transforming (in this example, a UML operation). It may also affect the processing that succeeds your rule, because your rule may have affected the behavior of the transform by changing the target of the transform, as you'll see in the next section.

In this case, you're inserting the rule as the second rule in ClassOperationTransform (indexing starts at 0). In the download file, we've given a brief overview of each transform and how rules are chained. It suffices to say here that when this rule eventually runs, a target C++ operation will have been created already in the abstract syntax tree mentioned earlier. This will be passed to your rule as your transformation target, and this is the data structure that you need to manipulate to get the behavior that you want.

3. Define the rule class

The final step for defining a new rule is to define the class that represents it. In this case, it represents the CPPMethodBodyRule class.

Listing 3 is a code snippet that shows how you can implement a rule class.

Listing 3. Example of implementing a rule class


public class CPPMethodBodyRule extends AbstractRule {
	public boolean canAccept(ITransformContext context) {
		Object object = context.getTarget();
		if (! (object instanceof CPPOwnedMethod))
			//this can happen if the method was not processed. 
//Then the target container would contain 
//the parent of the method.
			return false;
		return true;

	protected Object createTarget(ITransformContext context) 
throws Exception {
		System.out.println("Invoking extension");
		String newBody = new String ("//User defined body");
		Object object = context.getTarget();
		CPPOwnedMethod method = (CPPOwnedMethod)object;
		return method;


Any rule class must extend the AbstractRule class defined by the transformation framework. It should also override the canAccept and createTarget methods.

The canAccept method simply determines whether this rule can accept the transformation context that was passed to it. The transformation context is exactly what it claims to be: a context that maintains information about which element is being transformed (the ITransformContext.SOURCE object), what it is being transformed into (the ITransformContext.TARGET object), and where the transformation is going to place the transformed object (the ITransformContext.TARGET_CONTAINER object).

In this example, as you saw in the previous section, the rule is inserted into a ClassOperationTransform, and its transform source is a UML operation. However, the rule should not run for operations that this transformation is not going to handle. For example, if you have erroneously modeled operations inside of a template instantiation or an anonymous union, then such operations need to be thrown away. Here, the canAccept method checks whether a target object of type CPPOwnedMethod has already been created for this operation, which means that this operation can be handled safely. (More on CPPOwnedMethod later, when we talk about the C++ model.)

The second method is the createTarget method, where you get to change the generated target, which is what you set out to do. Here, for example, the method body is being set to a string of your choice. Notice that, in the canAccept method, you already checked to make sure that this rule is fired only if the target is a CPPOwnedMethod. Therefore, in the createTarget method that follows a call to canAccept during the transformation, you can safely assume that the target is, indeed, a CPPOwnedMethod.

Make sure that you return the modified CPPOwnedMethod from the createTarget method.

For two more examples and additional useful information, download the PDF file that accompanies this article.

Downloadable resources


Sign in or register to add and subscribe to comments.

ArticleTitle=Extending the UML to C++ transformation by using IBM Rational Software Architect 7.0