Skip to content

Papyrus UML support

Hawk includes specific support for MDT2 UML models and UML profiles developed using Papyrus UML. This can be used by enabling the UMLMetaModelResourceFactory and UMLModelResourceFactory plugins when creating a Hawk instance.

The implementation mostly reuses MDT UML2 and Papyrus UML as-is, in order to maximize compatibility. There are some minor caveats, which are documented in this page.

Supported file extensions

Hawk indexes plain UML2 models with the .uml extension, and Papyrus profiles with the .profile.uml extension. It does not index .di nor .notation files at the moment, as these do not provide semantic information.

.xmi files are not indexed by the Hawk UML components, to avoid conflicts with the plain EMF support (matching the file to the proper model resource is done strictly by file extension). You are recommended to rename your UML2 XMI files to .uml for now.

Predefined UML packages

UML2 provides an implementation of the UML standard libraries, with packages containing some common datatypes (e.g. String or Integer). If your models use any of these libraries, we heavily recommend that you add a PredefinedUMLLibraries component in your "Indexed Locations" section. Otherwise, any references from your models to the libraries will be left unresolved, and you will not be able to use those predefined entities in your queries.

This is because Hawk operates normally on files, and the predefined UML libraries are generally bundled within the UML2 plugins. The PredefinedUMLLibraries exposes those bundled resources to Hawk in a way that is transparent to the querying language.

Multi-version Papyrus UML profile support

Beyond registering all the metamodels required to index plain UML models, the UML metamodel resource factory in Hawk can register .profile.uml files as metamodels. This allows us to index UML models with custom profiles in Hawk.

Since UML profiles can be versioned, Hawk will register version X.Y.Z of profile with URI http://your/profile with http://your/profile/X.Y.Z as the URI. When querying with Hawk, you will have to specify http://your/profile/X.Y.Z in your default namespaces, in order to resolve the ambiguity that may exist between multiple versions of the same metamodel.

If a new version of the UML profile is created, you will need to register the .profile.uml file again with Hawk before it can index models that use that version of the profile. Hawk treats entities of different versions of the same profile as entirely different types.

In terms of implementation details, Hawk takes advantage of the fact that .profile.uml files contain a collection of Ecore EPackages. Hawk simply adds the /X.Y.Z version suffix to their namespace URI, and otherwise leaves them untouched.

Example: using Hawk to index all UML models in an Eclipse workspace

We will show how Hawk can be used to index all the UML models in an Eclipse workspace, including those that have custom profiles applied to them. To illustrate our approach, we will use these toy models created with Papyrus.

Models

The model is a very simple UML class diagram:

Sample UML class diagram

It only has two classes, one of which has the <<Special>> stereotype with a priority property equal to 23. This value is not shown in the diagram, but it can be checked from the "Profile" page of the "Properties" view when the class is selected.

The profile including the <<Special>> stereotype is also very simple:

Sample UML profile

The diagram imports the Class UML metaclass, and then extends it with the <<Special>> stereotype.

Creating the Hawk index

Before we can run any queries, we need to create a Hawk index. Please consult the core concepts documentation on how to do this. Some suggestions:

  • "Local Hawk" instances are the easiest to use when learning Hawk.
  • You will not need time-awareness for this example.
  • The SQLite backend offers good all-round performance, especially for fragmented models.
  • It is best to disable all plugins, and then only enable the UML metamodel and model resource factories. We will only need the EOL query engine for this article.
  • Min/Max Delay indicate how often will Hawk poll all the indexed locations. If you are only indexing the current workspace, you can leave both at 0 to disable polling: regardless of this setting, Hawk will react automatically whenever something in the workspace changes.

Once the index has been created, you should see an entry for it in the "Hawk" view:

Hawk view with one instance

Adding metamodels and models

From the screenshot above, we know that the index is RUNNING (available for queries) and not UPDATING nor STOPPED, so we can start configuring it as we need. First, we should double click on it to open the configuration dialog:

Hawk index config dialog

We should go to the "Metamodels" tab and click on "Add...", then select the specialThings.profile/model.profile.uml file. Hawk will register our custom profile as a metamodel, and we will be ready to index models using all the versions of this profile so far. Should we define any newer versions, we will have to add the file again to Hawk.

The dialog will now list the new metamodel:

Hawk index config dialog after registering profile

Now we are ready to add the locations where the models to be indexed are stored. We go to the "Indexed Locations" tab and click on "Add". First, we will add the predefined UML libraries with some commonly used instances (e.g. UML data types):

Adding UML predefined libraries

We need to pick the right "Type", and then click OK.

Next, we have to tell Hawk to index all the models in the workspace. We will "Add" another location, and this time fill the dialog like this:

Adding workspace

There are three variants of the workspace connector, using different sources of information for detecting if a file has changed. Typically, the workspace timestamp tends to be the most reliable if all your changes are being performed from Eclipse.

Hawk will spend some time UPDATING, and once it is RUNNING again we will be ready to run some queries on it.

Querying Hawk

We can finally query Hawk now. To do so, we need to select our index on the "Hawk" view and click on the "Query" button, which looks like a magnifying glass:

Query button in the Hawk view

We will see a dialog like this one, with all fields empty:

Query dialog

Enter the query return Class.all.name; and click on the "Run Query" button. This query lists the names of all the classes indexed so far by Hawk. You will notice that we obtain these results:

[E, T, MyClass, Special, V, NotSoSpecial, Stereotype1, K, E]

The E/T/V/K/E classes came from the predefined UML libraries. If you want only the results from your workspace, you must tell Hawk through the "Context Repositories" field in the "Path-based scope" tab, by entering platform:/resource. This is the base URI used by Hawk to identify all the files in your workspace. Click on "Run Query" again, and you should obtain the results shown in the screenshot:

[MyClass, Stereotype1, Special, NotSoSpecial]

Note how the query also returns the classes in the profile. Should you want to avoid this, you can either use the "Context Files" field (*model.uml will do this) to further restrict the scope of the query.

Finding UML objects by stereotype

If you would like to find all applications of stereotype X, you can simply use X.all and then use base_Metaclass to find the object that was annotated with that stereotype. For instance, this query will find the name of all the classes that had the <<Special>> stereotype applied to them:

return Special.all.base_Class.name;

You will get:

[MyClass]

You can also access stereotype properties:

return Special.all.collect(s|
  Sequence { s.priority, s.base_Class.name }
).asSequence;

This will produce:

[[23, MyClass]]

Finding stereotype applications from the UML object

If you want to go the other way around, you can use reverse reference navigation on those base_X references to find the stereotypes that have been applied to a UML object:

return Class.all
  .selectOne(s|s.name = 'MyClass')
  .revRefNav_base_Class
  .collect(st|Model.getTypeOf(st))
  .name;

This would produce:

[Special]