Building metamodels with the internal tools of EMF is tedious. Using the tree view based editors does not scale. Once you are at more than, say, 30 metaclasses, things become hard to work with. The same is true for the Ecore editor of GMF. Since you cannot easily factor a large metamodel into several diagrams, the diagram get cluttered and layouting becomes almost impossible.
One way to solve this problem is to use UML tools to draw the metamodel and then transform the UML model into an Ecore instance.
The oAW uml2ecore utiliy transforms a suitably structured Eclipse UML2 model (which can be created using various tools) into an Ecore file.
Note that this tool also serves as a tutorial for writing model-to-model transformations. This aspect, however, is documented elsewhere. This document only shows how to use the uml2ecore tool.
You need an installation of oAW 4.3 including the UML2 support. Run the UML2 example (available for download on the oAW download page http://www.openarchitectureware.org/staticpages/index.php/download) to verify that you have all the UML2 stuff installed.
The only additional thing required is that you install the UML2Ecore plugin into your Eclipse installation. The plugin is part of the oAW 4.3 distribution.
Of course, you could use the tree editors supplied by UML2 for
drawing the UML2 model that should be transformed into Ecore. However,
this is useless, since then you are back to square one: tree editors. So
you need to use an UML tool that is able to export the model in the
Eclipse UML2 2.0 format. For example, MagicDraw 11.5
(or above) can do that; this tool (MD 11.5) is also the one we tested the
uml2ecore
utility with.
Note that we do not use any profiles in the
uml2ecore
utility. Although using profiles might
make the metamodel a bit more expressive, we decided not to use a specific
profile, to reduce the potential for compatibility problems (with the
various tools).
You should first create a new Generator project (select File->New->Other->openArchitectureWare/Generator Project).
Then open your UML2 tool of choice (we will use MagicDraw here) and draw a class diagram that resembles your metamodel. Here is the one we have drawn as an example for this document:
This is the usual metamodel for entities and such. Nothing special. Also, the name of the model defines the name of the Ecore metamodel; here is the MagicDraw tree view to illustrate this:
You now have to save/export this model in Eclipse UML2 format. In
MagicDraw, you do this by selecting File → → . The exported files have to be in the root of
the src folder in the metamodel project created above. This is
how the project looks like after this step, assuming you would have called
your model entity.uml2
:
Before you can actually run the generator, you have to make sure that your project has a plugin dependency to the uml2ecore plugin. Double-Click on the plugin manifest file, select the Dependencies tab and click add. Select the org.openarchitectureware.uml2ecore plugin. The result looks like this:
In the current version, you also need a dependency to the
org.openarchitectureware.util.stdlib
project. Later
versions of the uml2ecore
plugin will reexport that
dependency, so that you will not need to add it you your projects
manually.
As usual, you have to write a workflow file. It also has to reside in your project source folder. Here is how it looks:
<?xml version="1.0"?> <workflow> <cartridge file="org/openarchitectureware/util/uml2ecore/uml2ecoreWorkflow.oaw" uml2ModelFile="org/openarchitectureware/uml2ecore/test/data/entity.uml2" outputPath="out" nsUriPrefix="http://www.voelter.de" includedPackages="Data" resourcePerToplevelPackage="false" addNameAttribute="false"/> </workflow>
As you might expect, it simply calls a cartridge supplied by uml2ecore plugin. You have to define the following properties:
Table 31. UML2Ecore - Cartridge properties
Property | Description |
---|---|
uml2ModelFile | The is the name of your UML2 file that contains the model; as usual, the file is looked for in the classpath (that is why you had to move it into the source folder) |
addNameAttribute | This determines whether automatic namespace management
and naming is turned on (see the end of this document). For the
simple example, please set the value to be false.
true/false |
nsUriPrefix | The nsUriPrefix is used to assemble the namespace URI. The name of the metamodel, as well as the nsPrefix, will be derived from the UML model name; so in our example, the nsPrefix and the name of the generated EPackage will be entitymm. The complete namespace URL also required by Ecore is created by concatenating the nsUriPrefix given here, and the name. The resulting namespace URL in the example will thus be http://www.voelter.de/entitymm. |
includedPackages | This property etermines which packages the transformer
should consider when transforming the model; note that the
contents of all the packages will be put into the root
EPackage . |
outputPath | This property etermines where the resulting files are written to. |
resourcePerToplevelPackage | It set to true , the generator will
write a separate Ecore file for each of the top level packages
in you UML model filtered by the value of the propery
includedPackages . Useful for modularizing
metamodels (see the end of this chapter). |
nameUnnamedNavigableAssociationEnds | If set to true , all unnamed navigable
association ends will be initialized with the name of the target
type. |
You can now run this workflow by selecting Run As -> oAW Workflow. The name of the generated Ecore file will correspond to the name of the root Model element in the UML model.
Here is a screenshot of the generated model:
The generator also creates a constraints file (called
entitymmConstraints.chk
) which contains a number of
constraints; currently these are specifically the checks for the minimum
multiplicity in references (an error is reported if the minimum
multiplicity is one, but the reference is null or empty, respectively).
You can integrate the generated constraints file into your workflow using
the usual approach.
It is often the case the basically all model elements in a model
should have a name. It might not always be necessary from a domain
perspective, but it is really useful for debugging. Of course, you can add
a superclass called Named
with a single attribute
name
to all you classes. However, if you set the
addNameAttribute
parameter to be true when calling
the uml2ecore cartridge, every class which does not inherit from another
class gets an additional name
attribute. Also,
constraints that make sure that names within a namespace (i.e. a
containment reference) are unique.
uml2ecore also comes with a utility extension called
org::openarchitectureware::util::stdlib::naming
that can calculate the namespace()
and the
qualifiedName()
for every model element that has
a name.
Another convenience is the parameter
nameUnnamedNavigableAssociationEnds
. Often you
specify a relationship between two metaclasses and give the target end the
name of the target metaclass. Let us say you specify an association from
metaclass Application
to
Component
. It is likely that the target end will be
called component
. By setting the parameter to
true all unnamed navigable association ends will get the name of the
target class. If there is more than one unnamed association only the first
one will be modified. This would lead to a failing constraint afterwards,
since at least after this modification all navigable association ends must
be named.