 | Level: Intermediate Jim Conallen (jconallen@us.ibm.com), Senior Software Engineer, IBM
18 Sep 2007 IBM® Rational® Software Architect is a tool for
architects and designers of software systems. It combines modeling with the Unified
Modeling Language (UML) and developer tools, including a code editor, compiler, and
debugger. For connecting these two phases, Rational Software Architect includes a
UML-to-Java™ 5 transformation that transforms UML models of classes,
interfaces, and enumerations into Java 5 source code. Although the functionality of
this transformation is complete, there are those who may want to extend this
transformation to include custom elements or conventions or to support a Domain
Specific-Language (DSL) extension that they have created. This article introduces
the basic elements of creating an extension to this transformation by presenting a
simple example.
About the Java 5
transformation feature in Rational Software Architect
IBM Rational Software Architect includes several predefined transformations, some
of which transform a UML model into source code. Version 7 introduced a
transformation that includes Java 5 features. Like its predecessor, this
transformation can be extended. This article walks you through the steps necessary
to create a simple example that demonstrates the core capabilities of this
functionality.
As with most features in Rational Software Architect, to extend it you use the
normal Eclipse extension mechanisms, creating a plug-in and implementing specific
extension points. This article does not address general plug-in development (see
Resources for recommended articles and examples).
Instead, it focuses on just enough plug-in development skills as necessary to
create and test a transformation extension.
Figure 1. Overall
transformation structure
When a transformation is invoked, it begins with the UML model, package, class,
interface, or enumeration element, depending on what was selected when the
transformation was invoked. The transformation will operate on the selected
element (or the entire model if no element is selected) and all of the elements
that it contains. This provides many opportunities for a transformation extension
author to tap into and contribute to the overall transformation. An extension
targets just one part of the overall transformation. You will find a complete list
of targets in the appendix (see Downloads).
For this simple example, you will define a simple class-level extension that
gets invoked each time that a transformation is ready to transform a UML class
element into a Java source file. This extension will do nothing more than look for
any keywords defined for this class and use them to add a new Javadoc tag
associated with the class in the source code.
You will follow these steps:
- Create a new plug-in project for your transformation extension. This project
may be combined with other extensions; however, to make this example simple, you
will create a new project from scratch.
- 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 your extension.
- Define the extension point in the plug-in descriptor, which identifies the
transformation that you are extending and the point within it that you want to
make a contribution.
- Provide a Java implementation of the class rule that will be invoked each
time that the transformation encounters the element type that you indicate.
- Test the transformation by invoking a runtime workbench and running a
UML-to-Java 5 transformation.
Step 1. Create and
configure a new plug-in project
The transformation extension begins with a new plug-in project:
- Select the menu File > New > Project, and select
Plug-in Project from the list of project types, and click Next.
- 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 2.
Figure 2. New Plug-in Project
wizard
- On the second page (Figure 3), update the default Plug-in Name field; the
remaining defaults are acceptable.
Figure 3. New Plug-in Project
wizard Properties page
On the final page, it is not necessary to select a plug-in template, because
there are no templates for transformation extensions, just for new complete
transformations.
Figure 4. New Plug-in Project
wizard final page
- Uncheck the template option and click the Finish button (Figure 4.)
Figure 4. New Plug-in Project wizard final page When the wizard finishes, it
creates a new plug-in project and opens the Overview page of the project
descriptor (Figure 5).
Figure 5. New project structure
Step 2. Add required
plug-in dependencies
The next step is to add a few plug-ins as dependencies. These plug-ins contain
the extension point definitions for the implementation plus APIs that will be used
in the implementation of the extension.
- Switch to the Dependencies tab (at the bottom of this editor), and add
the following plug-ins as dependencies:
- com.ibm.xtools.modeler
- com.ibm.xtools.transform.core
- com.ibm.xtools.transform.uml2.java5
- org.eclipse.jdt.core
- Verify that the plug-ins were added. The resulting list should look similar
to Figure 6.
Figure 6. Dependent plug-ins
Step 3. Define and
implement the extension
The next step is to declare that you are going to provide an implementation to
one or more extension points.
Switch to the Extensions tab, and click the Add button to add a new extension.
You will then see the dialog box that Figure 7 shows. Enter this extension point:
com.ibm.xtools.transform.core.transformationExtensions. When you find it, select
it and click Finish.
Figure 7. New Extension dialog
- There is more information that needs to be supplied in the extension point
definition. These values can be entered through the user interface on the
Extension tab page. However, most people find it easier to just edit the
raw XML of 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 file,
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 version="1.0.0"
name="My UML to Java5 Transformation Extensions"
enabled="true"
targetTransformation=
"com.ibm.xtools.transform.uml2.java5.internal.UML2JavaTransform"
id="com.ibm.rsa.java5.extension.sample.transformationExtensions.id">
<RuleDefinition
name="Javadoc Keyword Rule"
class="com.ibm.rsa.java5.extension.sample.ClassKeywordJavadocRule"
id="com.ibm.rsa.java5.extension.sample.ClassKeywordJavadocRule.id"/>
<ExtendTransform
targetTransform=
"com.ibm.xtools.transform.uml2.java5.internal.ClassTransform">
<AddRule id="com.ibm.rsa.java5.extension.sample.ClassKeywordJavadocRule.id"/>
</ExtendTransform>
</TransformationExtension>
</extension>
|
The body of an extension element is defined by the extension point provider. In
this case, that is the Rational Software Architect Core Transformation component.
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.
- For this simple example, specify a targetTransformation with the ID of
com.ibm.xtools.transform.uml2.java5.internal.UML2JavaTransform.
- Supply appropriate values for the ID: version number, name, and the Boolean
value for enabled.
Each transformation extension defines, as child elements. a rule definition that
specifies a class that implements a rule interface. This class does most of the
work of contributing to the transformation. It is defined as a separate element,
because it is possible for a single class to contribute to multiple parts of a
transformation (for example, class, property, and operation).
After all of the rules have been defined, they are matched with the part of the
transformation that they will contribute to with ExtendTransform elements. This
element defines the part of the transformation that a rule will contribute to. For
this, you want to contribute to the extension each time a new class is being
transformed, and the value of the targetTransform should be
com.ibm.xtools.transform.uml2.java5.internal.ClassTransform.
The appendix that you can download from this article page contains a complete
listing of target transformation ID (see Resources).
Given that 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 have only one rule defined in your project as you are using it here.
Note:
The ID of the rule in the RuleDefinition element must match
that of the AddRule element.
Step 4. Provide a Java
implementation of the class rule
When you are finished editing this file, save it. You should notice a small
error icon in the left 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 double-click the icon and select the prompt to Create a new
class. This invokes the New Class wizard, which you can use it to
create the class file. You can also manually create the class with the File
> New menu. Whichever way you prefer to create Java classes in
Eclipse, create one in the designated package and with the correct name.
- To make the implementation as easy as possible, extend the Rational Software
Architect transformation framework class
com.ibm.xtools.transform.uml2.imple.internal.java5.ClassRule.
With this class as a super class, there are only three methods that should be
overridden.
-
canAccept
-
createTarget
-
isSourceConsumed
Each of these methods accepts a transformation context object that can be queried
to gain access to the source and target objects. With this information, you can
decide whether you want to process this particular class
(canAccept) or whether further processing of 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.
- For this simple transformation extension, return False in the isSourceConsumed
method to indicate that further processing should continue, and that the
extension does not mean that all of the work of the transformation of this
particular element was completed.
- Indicate that you can accept a particular class when there is at least one
keyword defined. If there are no keywords, there is nothing for you to do, so
indicate that you are not interested. You can access the source UML class
through the transformation context that is passed in as an argument. The
getSource() method on the context should return a
reference to a UML class, since we specified in the extension point that we were
interested only in Class transforms.
- Type the source object to a UML class, and then get the keyword list from it.
If the list is empty, then return
False.
If there are keywords, then you can expect the
createTarget() method to be called. This is where you
are expected to make your contribution to the transformation.
Listing 2. Java rule class implementation
package com.ibm.rsa.java5.extension.sample;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.uml2.uml.Class;
import com.ibm.xtools.transform.core.ITransformContext;
import com.ibm.xtools.transform.uml2.impl.internal.java5.ClassRule;
public class ClassKeywordJavadocRule extends ClassRule {
protected Object createTarget(ITransformContext context) {
TypeDeclaration target = (TypeDeclaration) context.getTarget();
Javadoc javadoc = target.getJavadoc();
List tags = javadoc.tags();
AST ast = target.getAST();
Class umlCls = (Class) context.getSource();
EList keywords = umlCls.getKeywords();
for (Iterator iter = keywords.iterator(); iter.hasNext();) {
String keyword = (String) iter.next();
TagElement tag = ast.newTagElement();
tag.setTagName("@" + keyword);
tags.add(tag);
}
return target;
}
public boolean isSourceConsumed(ITransformContext context) {
return false;
}
public boolean canAccept(ITransformContext context) {
Class umlCls = (Class) context.getSource();
return umlCls.getKeywords().size()>0;
}
} |
The first contribution that developers usually make when implementing the
createTarget() method is to access the source and target objects for this part of
the transformation. Because you have 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) TypeDeclaration object. The Eclipse Java Developer
Tools (JDT) project provides all of the APIs for manipulating Java source code.
The AST is the object model that parses source code for Eclipse. When this method
is invoked, a new TypeDeclaration object is created by the transformation
framework. It should be noted that if there is any existing Java source code, this
is not made available in the target object. Merging of the newly generated code
with any existing code happens after all of the model elements have been
transformed.
Discussing the AST in detail is beyond the scope of this article, other than to
say that when the extension needs to add new elements to the generated source
code, that is done by calling methods on the AST objects. For this simple example,
you will just add a new Javadoc tag to the class definition's Javadoc
comments. Javadoc is an object type in the AST, and is obtained from the
TypeDeclaration object.
- From the Javadoc object, get the list of tags that make up this
Javadoc. You can add (or remove) tags from the generated source code.
- To determine which tag to add, access the source object from the
transformation context and cast it to a UML class. From this object, you get the
list of keywords (similar to how you did that when implementing the canAccept()
method).
- With this list, iterate over the keywords and create a new TagElement for each
keyword.
- Set the name of the tag to the value of the keyword (prefaced with the @
symbol).
- Finally, add the new tag to the list of tags for the class Javadocs.
The method must return a target object. Usually, it is the same object that was
passed in as a target. This completes the creation and implementation of your
transformation extension.
Step 5. Test the extension
Now that you have defined and implemented the extension, it's time to test it.
You do this by creating a new debug configuration that opens a new runtime
workbench.
- To create this configuration, select Run > Debug from the main
menu.
- In the dialog, create a new Eclipse application configuration and name it
appropriately.
Tip:
You can change the location of the workspace, and it is usually
a good idea to choose a new location to avoid inadvertently affecting an existing
workspace.
- Leave the remaining default settings as they are (Figure 8).
Figure 8. New debug
configuration
- When you have finished editing the configuration, click Debug to start
a new instance of Eclipse. This new instance will have the transformation
extension that you just created installed as a plug-in.
- To test the extension, you need to create a new Java project and a UML model
in that project within the new instance of the Eclipse shell.
- Within the UML model, create a test package and class, and include a few
attributes or operations. The resulting workspace should look something like
that of Figure 9.
Figure 9. Test project in
runtime workbench
Next, you need to add a keyword to the class, because your extension will work
only on a class that has a keyword defined.
- Select the class and then the Stereotypes sub-tab in the
Properties view.
- Enter text in the Keywords field (Figure 10).
Figure 10. Setting the keywords property
With the test project and model set, you now need to create a new transformation
configuration that defines the main parameters of the UML-to-Java 5
transformation.
- Select File > New from the main menu.
- Under the Transformations category, select Transformation
Configuration, and then click Next (Figure 11.).
Figure 11. Creating a new
transformation configuration
- There are many transformation types that are included with Rational Software
Architect. Choose the UML-to-Java 5 transformation (Figure 12), because that is
the one for the extension that you created.
- Give it an appropriate name. This will be used as the file name of the
configuration in the Java project.
Figure 12. Selecting the transformation
- The first things that you need to specify in a transformation configuration
are the source model and the target project.
- Be sure to select the UML model, because it appears under the Models
virtual folder, and not the actual .emx file.
- Select the target by choosing the Java project itself (Figure 13).
Figure 13. Setting the source and targets for the transformation
- Optionally, you can turn off the automatic generation of get and set methods
on the next configuration setup page (Figure 14), and experiment with the other
options on the remaining pages. However, for this simple test, accepting the
defaults clicking Finish should work.
Figure 14. Transformation
configuration options
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.
- To invoke the transformation and your extension to it, select the class in the
diagram, invoke the popup menu, and select Transform > My UML to Java5
Transform Test.tc > UML to Java V5.0 (Figure 15).
Figure 15. Invoking the
transformation
Figure 16 shows the results of the transformation. You can see that a Javadoc
tag that matches the value of the keyword that you added is included in the
Javadocs of the class.
Figure 16. Results of
generated code
Conclusion
This simple example of a UML-to-Java 5 extension was meant just to demonstrate
the basic mechanism for creating extensions. Real-life extensions would most
likely be more intelligent, using information in the model and making more
interesting changes to the generated code or even adding companion artifacts to
the source code. The important point to take away from this article is that
creating extensions to existing transforms makes a lot of 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 (deployment descriptors or companion files, for
example) also simplifies the development process, because you need to run only one
transformation rather than many.
Download | Description | Name | Size | Download method |
|---|
| List of transformation targets | UML_to_Java5_transformation_targets.pdf | 32KB | HTTP |
|---|
Resources Learn
-
An introduction to the AST, the base framework for many powerful tools of the Eclipse IDE, including refactoring, Quick Fix and Quick Assist.
-
Exploring Eclipse's ASTParser, an article that teaches how to use the parser to generate code.
-
"Developing Eclipse plug-ins,"
an article by David Gallardo about how to create, debug, and install your plug-in
(developerWorks, December 2002).
-
Eclipse Corner Articles by Eclipse
developers on the Eclipse.org Web site.
-
"Eclipse Plugins Exposed, Part 1: A First Glimpse"
(onJava.com, February 2005).
-
"Building
templates with the Eclipse Plug-in Development Environment"
by Chris Aniszczyk (developerWorks, February 2007).
-
"Notes on the Eclipse Plug-in Architecture"
(Eclipse.org, July 2003).
-
"How to Internationalize your Eclipse Plug-In,"
a roadmap for writing Eclipse plug-ins destined for the international market
(Eclipse.org, August 2002).
- Read Contributing to Eclipse: Principles,
Patterns, and Plugins, a book by by Erich Gamma and Kent Beck
(Addison-Wesley Professional, October 31, 2003).
- Visit the
Rational software area on developerWorks
for technical resources and best practices for Rational Software Delivery Platform
products.
- Subscribe to the
developerWorks Rational zone newsletter.
Keep up with developerWorks Rational content. Every other week, you'll
receive updates on the latest technical resources and best practices for the
Rational Software Delivery Platform.
- Browse the
technology bookstore
for books on these and other technical topics.
Get products and technologies
Discuss
About the author  | 
|  | Jim Conallen is a software engineer in IBM Software Group's Rational Model-Driven Development Strategy team, where he is actively involved in applying the Object Management Group's (OMG) Model-Driven Architecture (MDA) initiative to IBM's Rational model tooling. Jim is a frequent conference speaker and article writer. His areas of expertise include Web application development, where he developed the Web Application Extension for UML (WAE), an extension to the UML that lets developers model web-centric architectures with UML at appropriate levels of abstraction and detail. This work served as the basis for IBM Rational Rose and IBM Rational XDE Web Modeling functionality. |
Rate this page
|  |