With the Xtext framework, you can build DSL workbenches in just a few steps. However, sometimes you want to reuse model elements already defined in other formats or even in other languages. In this blog post I’m going to demonstrate typical scenarios when you’re reusing model elements belonging to a different language.
Firstly, consider having some pre-defined Eclipse UML2 models, and you want to reference classes of these UML models from your Xtext DSL.
If you are interested in reusing model elements belonging to the same language but defined in different formats, take a look at my previous blog post “Combining EMF models with Xtext DSLs”.
Let's get started with the preparatory steps:
Preparatory steps
- Install the latest version of the UML2 Extender SDK and the Xtext Complete SDK of the Eclipse release train.
- Create the Domainmodel project, based on the Xtext 15 Minutes Tutorial. The meta-model of the Domainmodel project
describes that a domain model consist of certain types (data types and entities), an entity contains features and each feature can have a type. To be able to use UML classes in the feature's type definition, the following modifications are necessary:
Modifications1 in the org.example.domainmodel plug-in
- Extend the Domainmodel.xtext grammar definition:
grammar org.example.domainmodel.Domainmodel with org.eclipse.xtext.common.Terminals ... import "http://www.eclipse.org/uml2/5.0.0/UML" as uml ... Feature: (many?='many')? name=ID ':' type=[uml::Class|FQN] | type=[Type]; ...
- Extend the GenerateDomainmodel.mwe2 workflow:
module org.example.domainmodel.GenerateDomainmodel import org.eclipse.emf.mwe.utils.* import org.eclipse.xtext.xtext.generator.* import org.eclipse.xtext.xtext.generator.model.project.* var rootPath = ".." Workflow { bean = StandaloneSetup { scanClassPath = true platformUri = rootPath uriMap = { from = "platform:/plugin/org.eclipse.emf.codegen.ecore/model/GenModel.genmodel" to = "platform:/resource/org.eclipse.emf.codegen.ecore/model/GenModel.genmodel" } uriMap = { from = "platform:/plugin/org.eclipse.emf.ecore/model/Ecore.genmodel" to = "platform:/resource/org.eclipse.emf.ecore/model/Ecore.genmodel" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.codegen.ecore/model/GenModel.genmodel" to = "platform:/resource/org.eclipse.uml2.codegen.ecore/model/GenModel.genmodel" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.uml/model/UML.genmodel" to = "platform:/resource/org.eclipse.uml2.uml/model/UML.genmodel" } uriMap = { from = "platform:/plugin/org.eclipse.emf.codegen.ecore/model/GenModel.ecore" to = "platform:/resource/org.eclipse.emf.codegen.ecore/model/GenModel.ecore" } uriMap = { from = "platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore" to = "platform:/resource/org.eclipse.emf.ecore/model/Ecore.ecore" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.codegen.ecore/model/GenModel.ecore" to = "platform:/resource/org.eclipse.uml2.codegen.ecore/model/GenModel.ecore" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.uml/model/UML.ecore" to = "platform:/resource/org.eclipse.uml2.uml/model/UML.ecore" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.types/model/Types.genmodel" to = "platform:/resource/org.eclipse.uml2.types/model/Types.genmodel" } uriMap = { from = "platform:/plugin/org.eclipse.uml2.types/model/Types.ecore" to = "platform:/resource/org.eclipse.uml2.types/model/Types.ecore" } registerGeneratedEPackage = "org.eclipse.emf.ecore.EcorePackage" registerGeneratedEPackage = "org.eclipse.uml2.uml.UMLPackage" registerGeneratedEPackage = "org.eclipse.uml2.types.TypesPackage" registerGeneratedEPackage = "org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage" registerGeneratedEPackage = "org.eclipse.uml2.codegen.ecore.genmodel.GenModelPackage" registerGenModelFile = "platform:/resource/org.eclipse.emf.ecore/model/Ecore.genmodel" registerGenModelFile = "platform:/resource/org.eclipse.emf.codegen.ecore/model/GenModel.genmodel" registerGenModelFile = "platform:/resource/org.eclipse.uml2.uml/model/UML.genmodel" registerGenModelFile = "platform:/resource/org.eclipse.uml2.codegen.ecore/model/GenModel.genmodel" } component = XtextGenerator { ... } }
- Add the following plugins to the Require-Bundle section in the MANIFEST.MF file:
- org.eclipse.uml2.uml
- org.eclipse.uml2.codegen.ecore
- Add the following classes:
Modifications2 in the org.example.domainmodel.ui plug-in
- Add the following classes:
- Register DomainmodelActivatorEx as Bundle-Activator in the MANIFEST.MF file.
- Add the following plugin to the Require-Bundle section in the MANIFEST.MF:
- org.eclipse.emf.ecore.editor
- Add the following section to the plugin.xml file:
<!-- register the Xtext UI language services to Xtext's registry --> <extension point="org.eclipse.xtext.extension_resourceServiceProvider"> <resourceServiceProvider class="org.example.domainmodel.ui.UMLExecutableExtensionFactory:org.eclipse.xtext.ui.resource.generic.EmfResourceUIServiceProvider" uriExtension="uml"> </resourceServiceProvider> </extension>
Manual testing
Start an Eclipse runtime to verify that parsing, linking, content assistant, hovering, hyperlink navigation, quickfixes, etc., are working properly.
Automated testing
- Extend the org.example.domainmodel.tests plug-in by the Indexing, Linking, Parsing, Scoping, etc. JUnit test cases.
- Extend the org.example.domainmodel.ui.tests plug-in by the ContentAssistant, Hovering, Hyperlinking, Quickfixes, etc. JUnit Plug-in test cases.
Conclusion
We have done the preparatory steps, modifications and testing necessary to reuse model elements belonging to a different language. This example has been kept simple on purpose.
If you are interested in more advanced use-cases on the Xtext/UML integration, I recommend Karsten‘s and Holger‘s presentation on “How to build Code Generators for Non-Xtext Models with Xtend”.
Do you have questions or feedback? Feel free to leave me a comment below.
1,2 Please note that the blog post “Combining EMF Models with Xtext DSLs” explains the necessary modifications in detail.
Comments