UML2 Example

Setting up Eclipse
Setting up the project
Creating a UML2 Model
Code generation
Profile Support

Setting up Eclipse

Before you can use oAW with Eclipse UML2, you first have to install the UML2 plugins into your Eclipse installation.

Setting up the project

Create a new openArchitectureWare plugin project. You have to add the following dependencies to the manifest file:

  • org.openarchitectureware.uml2.adapter

To tell the oAW Eclipse plugins that this project is a UML2 specific one, you need to specify that in the oAW preferences. Open the project properties, select the openArchitectureWare tab and select the UML2 profiles metamodel.

Figure 56. Configure UML2 profiles metamodel

Configure UML2 profiles metamodel


Note that if you want to transform an UML2 model into a normal EMF model, you need to add the UML2 metamodel and the EMF metamodels. The order of profiles is important! The UML2 profiles entry must be first in the list.

Creating a UML2 Model

You start by defining a UML2 model, i.e. an instance of the UML2 metamodel. In the new Java project, in the source folder, you create a UML2 model that you should call example.uml.

Figure 57. Creating a new UML2 model

Creating a new UML2 model

You then have to select the model object. Make sure its a Model, not a Profile.

Figure 58. Selecting the Model object

Selecting the Model object


Modelling the content

You should then build a model that looks somewhat like this:

Figure 59. Example model

Example model


By the way, if you rename the .uml file to .ecore, you can edit the model using the ecore editors. To inspect the model, they provide a somewhat better view, so you might try!

Code generation

Defining the templates

Inside the source folder of our project, create a templates package. Inside that package folder, create a template file Root.xpt that has the following content. First, we define the entry template that is called Root. Since we expect a UML model element to be the top element to the model, we define it for uml::Model. Note the use of the uml Namespace prefix, as defined in the UML2 metamodel. Inside that template, we iterate over all owned elements of type uml::Package in the model and expand a template for the packages defined in it.

«DEFINE Root FOR uml::Model»
  «EXPAND PackageRoot FOREACH allOwnedElements().typeSelect(uml::Package)»
«ENDDEFINE»

In the package template, we again iterate over all owned elements and call a template that handles classes. Although we only have classes in that package we could not rely on this in general. The package may contain any other packageable element, so we need to filter classes using typeSelect().

«DEFINE PackageRoot FOR uml::Package»
  «EXPAND ClassRoot FOREACH ownedType.typeSelect(uml::Class)»
«ENDDEFINE»  

This template handles classes. It opens a file that has the same name as the class, suffixed by .java. Into that file, we generate an empty class body.

«DEFINE ClassRoot FOR uml::Class»
  «FILE name+".java"»
    public class «name» {}
  «ENDFILE»
«ENDDEFINE» 

Defining the workflow

In order to generate code, we need a workflow definition. Here is the workflow file; you should put it into the source folder. The file should be generally understandable if you read the oAW Tutorial chapter.

<?xml version="1.0" encoding="ISO-8859-1"?>
<workflow>

You need to setup the UML2 stuff (registering URI maps, Factories, etc.). This can be done declaring a bean in before of the XmiReader component:

<bean class="oaw.uml2.Setup" standardUML2Setup="true"/> 

<component class="oaw.emf.XmiReader">
  <modelFile value="example.uml"/>
  <outputSlot value="model"/>
</component>

The XmiReader reads the model and stores the content (a list containing the model element) in a slot named 'model'. As usual, you might want to clean the target directory.

<component id="dirCleaner"
  class="oaw.workflow.common.DirectoryCleaner"
  directory="src-gen"/>

and in the generator we also configure the UML2 metamodel.

<component id="generator" class="oaw.xpand2.Generator" skipOnErrors="true">
  <metaModel class="oaw.uml2.UML2MetaModel"/>
  <expand value="templates::Root::Root FOR model"/>
  <fileEncoding value="ISO-8859-1"/>
  <outlet path="src-gen">
   <postprocessor class="oaw.xpand2.output.JavaBeautifier"/>
  </outlet>
</component>

If you run the workflow (by right clicking on the .oaw file and select Run AsoAW workflow) the two Java classes should be generated.

Profile Support

openArchitectureWare 4 is shipped with a special UML2Profiles metamodel implementation. The implementation maps Stereotypes to Types and Tagged Values to simple properties. It also supports Enumerations defined in the profile and Stereotype hierarchies.

Defining a Profile

To define a profile, you can use a variety of UML2-based modelling tools. Assuming they do actually correctly create profile definitions (which is not always the case, as we had to learn painfully), creating a profile and exporting it correctly is straight forward.

In this section, we explain the "manual way", which is good for explaining what happens, but completely useless for practical use. You do not want to build models of realistic sizes using the mechanisms explained below.

You start be creating a new UML2 file (as shown above). In the example we will call it test.profile.uml. The root element, however, will be a Profile, not a Package. Don't forget to actually assign a name to the profile! It should be test, too.

As a child of that Profile, you then create a Packaged Element Stereotype (you will have to scroll a bit in the Add Child menu....). For the sake of example, we will call it test, too. In our case, we want to make the stereotype be applicable to UML classes – they are defined as part of the UML2 metamodel. So we have to import that metamodel first. So what you do is to select your profile object, and then go to the UML2 Editor menu (in the Eclipse menu bar) and select Profile -> Reference Metaclass. Select uml::Class. You can then select your stereotype, and select Stereotype -> Create Extension from the UML2 Editor menu. Select uml::Class. This should lead to the following model. Save it and you are done with the profile definition.

Figure 60. Modelling a Profile

Modelling a Profile

Applying the Profile

To make any use of the profile, we have to apply it to some kind of model. To do that, we copy the example.uml model to a example-profiled.uml. We then open that file and load a resource, namely the profile we just defined. This then looks somewhat like this:

Figure 61. Loading the Profile

Loading the Profile

Now, to make the following stuff work, you first have to select the profile and select the Profile -> Define operation from the UML2 Editor menu. This creates all kinds of additional model elements, about which you should not care for the moment.

Now, finally, you can select your cars package (the one from the example model) and select Package -> Apply Profile from the UML2 Editor menu. Select your test profile to be applied.

For the purpose of this example, you should now apply the test stereotype to the PersonCar class. Select the class, and the select Element -> Apply Stereotype from the UML2 Editor menu. This should result in the following model:

Figure 62. Defining the Profile

Defining the Profile

Generating Code

Note that all the stuff above was not in any way related to oAW, it was just the "bare bones" means of creating and applying a profile to a UML2 model. Having an UML2 tool capable of storing models as EMF UML2 XMI would make the creation of the model far more easier. Since we cannot assume which UML2 tool you are using this tutorial shows you this way, which would always work without further tooling installed.

There are two things we have to change: The workflow (specifically, the configuration of the generator component) needs to know about the profile, and the template needs to generate different code if a class has the test stereotype applied. Let us look at the second aspect first. Here is the modified template (in RootWithProfile.xpt):

«DEFINE Root FOR uml::Model»
  «EXPAND PackageRoot FOREACH allOwnedElements().typeSelect(uml::Package)»
«ENDDEFINE»
 
«DEFINE PackageRoot FOR uml::Package»
  «EXPAND ClassRoot FOREACH ownedType.typeSelect(uml::Class)»
«ENDDEFINE»  
    
«DEFINE ClassRoot FOR uml::Class»
  «FILE name+".java"»  
    public class «name» {}
  «ENDFILE»
«ENDDEFINE» 

«DEFINE ClassRoot FOR test::test»
  «FILE name+".java"»  
    public class «name» {} // stereotyped
  «ENDFILE»
«ENDDEFINE»  

As you can see, the stereotype acts just like a type, and even the polymorphic dispatch between the base type (uml::Class) and the stereotype works!

Adapting the workflow file is also straight forward (workflowWithProfile.oaw), here is the modified generator component:

<component id="generator" class="oaw.xpand2.Generator" skipOnErrors="true">
  <metaModel class="oaw.uml2.UML2MetaModel"/>
  <metaModel id="profile"
    class="oaw.uml2.profile.ProfileMetaModel">
    <profile value="test.profile.uml"/>
  </metaModel>
  <expand
    value="templates::RootWithProfile::Root FOR model"/>
  <outlet path="src-gen">
      <postprocessor class="oaw.xpand2.output.JavaBeautifier"/>
  </outlet>
</component>

The only thing, we have to do is to add a new metamodel that references the profile we just created.