If you often find yourself customizing the Ecore model with operations or redefining existing methods, you might want to try
Xcore for maintaining your Ecore model. Xcore is a DSL for Ecore. It is implemented in Xtext and Xbase, allowing you to specify the Ecore model with a Java-like syntax. Besides the structure of the model, you can also use Xcore to specify the behavior of operations, derived features, and provide custom implementation of methods. Moreover, the mapping of data types to Java types is more straightforward in Xcore. Being a DSL based on Xtext/Xbase, with Xcore you do not need a .genmodel
file to generate the Java code for your model; you just edit your file, and the Java code will be automatically generated on save.
In order to use Xcore, you need to install the feature "EMF - Eclipse Modeling Framework Xcore SDK" into your Eclipse, unless it is already installed.
In this section, we will implement a new Xtext DSL, for specifying libraries, books, and authors. The Ecore model will be specified with Xcore.
Let's create an Xcore project for defining the Ecore model for the DSL that we are going to implement in this section. Steps are as follows:
org.example.library.model
as the project name and press Finish.In the model directory of the project, create an .xcore
file, for example, Library.xcore
. Here we define the Ecore model, using a Java-like syntax:
package org.example.library class LibraryModel { contains Library[] libraries } abstract class Named { String name } class Library extends Named { contains Writer[] writers contains Book[] books } class Writer extends Named { } class Book { String title refers Writer[] authors }
As soon as you save the file, the Java code for the Ecore model will be automatically generated in the src-gen
folder. Containment references are specified with the keyword contains
, while cross-references with the keyword refers
. The fact that a reference is a multi-value reference is specified with the []
. Of course, you can use the content assist while editing an Xcore file. The documentation of Xcore is https://wiki.eclipse.org/Xcore.
We will now create a new Xtext project starting from the Ecore model implemented in Xcore.
In order to create the Xtext project, perform the following steps:
Library.xcore
and press OK.LibraryModel
for the Entry rule.org.example.library
org.example.library.Library
library
As in the School DSL, the first part of the grammar uses an import
line to refer to the existing Ecore model:
grammar org.example.library.Library with org.eclipse.xtext.common.Terminals import "org.example.library"
We replace the rules of
the Library.xtext
grammar completely with these grammar rules:
LibraryModel returns LibraryModel: libraries+=Library*; Library returns Library: 'library' name=STRING '{' ('writers' '{' writers+=Writer ( "," writers+=Writer)* '}' )? ('books' '{' books+=Book ( "," books+=Book)* '}' )?' Writer returns Writer: name=STRING; Book returns Book: 'title' title=STRING ('authors' authors+=[Writer|STRING] ( "," authors+=[Writer|STRING])* )? ;
Valid programs for this DSL have the following shape:
library "A library" { writers { "A writer", "Another writer", "Third writer" } books { title "A book", title "Another book" authors "Third writer", "A writer" } } library "An empty library" { }
Remember to fix the
LibraryStandaloneSetup
before running any JUnit tests:
override register(Injector injector) if (!EPackage.Registry.INSTANCE.containsKey(LibraryPackage.eNS_URI)) { Epackage.Registry.INSTANCE.put(LibraryPackage.eNS_URI, LibraryPackage.eINSTANCE); } super.register(injector) }
As in the previous DSLs, let's assume that we want to modify the Ecore model. Since we use Xcore, everything will be much easier since we can write a model operation directly in the Xcore file, keeping in mind that the body of an operation in Xcore uses the Xbase syntax. For example, we add a datatype Books
that corresponds to the Java type Iterable<Book>
, and we implement in the Writer
class the get
operation:
type Books wraps Iterable<Book> class Writer extendsextends Named { op Books getBooks() { (eContainer as Library).books. filter[authors.contains(this)] } }
That's all we need to do. On saving the file, Xcore will automatically regenerate the Java code. We do not need to modify the generate code and specify the Javadoc @generated NOT
, neither we need to create a separate Java file with ImplCustom
.
The same holds if we want to give a custom implementation of methods such as toString
. Differently from what we had to do in the previous example DSL, with Xcore it's just a matter of writing:
class Book { ... op String toString() { 'title: "' + title + '"' + if (!authors.empty) ", by " + authors.map[name].join(", ") } }
We can use the added operation in the validator, in the code generator, and in other parts of the DSL. This is left as an exercise.
Building an Xtext DSL that is based on Xcore with Maven/Tycho requires some ad-justments to the pom files. You can also generate the Java code from the Xcore model during the Maven build, using the xtext-maven-plugin. In the source code of the examples of the book, you will find all the pom files for this example DSL configured to use Xcore. Further and advanced details about using Xcore with Xtext can be found in the presentation Schill 2015.
3.12.161.77