You are on page 1of 144

Language Workbench Competition 2011 Xtext Submission

Version: 1.2 Final - 18. Mai 2011

Karsten Thoms (itemis)

LWC11 -

Submission

Abstract
The Language Workbench Competition 2011 (LWC11) is an initiative created by a group of experts at the CodeGeneration 2010 conference1 . The aim is to set a common task2 for Language Workbenches3 which is implemented with the dierent existing alternatives in a comparable way. This document describes in detail how the task is solved with Xtext4 . Xtext is one of the most well known Language Workbenches and part of the Eclipse Modeling Project5 .

Testimonial
This project was developed with the help of some colleagues at itemis, and I am grateful for their help: 1. Sven Etinge contributed the Instance DSL. Without his knowledge it would be very hard to implement this project already with Xtext 2.0 in this pre-release state. 2. Rainer Klute has written the initial version of the introduction. Although at that time the task was realized with Xtext 1.0.2 and many places have been reworked, major parts of the Overview and Phase 0.1 descriptions are based on his contribution. 3. Karsten Nolte and Alexander Hannweg initially transformed the Wiki pages to LaTeX. 4. The Xtext Development Team was always responsive for questions , provided with the Domainmodel example a good template to create major parts of the implementation and was eager to x detected bugs. 5. Detailed feedback helped to improve the document. My thanks for reading the document carefully and providing input go to
6

: Markus Vlter, Jrg Reichert, Joel W. Denton

1 2

http://www.codegeneration.net/cg2010/ see http://www.languageworkbenches.net/ for the detailed description of the LWC11 competition and other submissions http://martinfowler.com/articles/languageWorkbench.html, http://blog.efftinge.de/2007/11/

definition-of-term-language-workbench.html http://www.xtext.org 5 http://www.eclipse.org/modeling 6 Not all from itemis


4

LWC11 -

Submission

Document History
Version 1.0 - 2011-05-01 Initial submission for the Language Workbench Competition 2011

Version 1.1 - 2011-05-13 Complete review of the document, signicant changes to structure and content Added missing code for Phase 1.3 (M2M trafo) Updated code examples to latest grammar changes of the Xtend language Improved formatting for class/method/le names, menu items etc. Version 1.2 - 2011-05-18 Included feedback from Markus Vlter, Jrg Reichert, Joel Denton Added footnotes with links to code in SVN for all major code snippets Changed formatting for listings: added frame around source snippets; changed colors to Eclipse style; added language denitions for Xtext, Xtend, Mwe2 for better syntax coloring Improved code examples for Headless Generators Added document history Layouting: Added logos to header, changed title page Added sections for Phase 2.1 and 2.2 Introduced UI Tweaking section in Phase 3 - Outline and Quickx

LWC11 -

Submission

Table Of Contents
1 Introduction 1.1 Task Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 1.1.2 1.1.3 1.1.4 1.2 1.3 1.4 Phase 0 - Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Phase 1 - Advanced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Phase 2 - Non-Functional . . . . . . . . . . . . . . . . . . . . . . . . . . Phase 3 - Freestyle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 8 9 9 9

Xtext Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Installing Xtext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Workspace Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 13

2 Phase 0.1 - Basics 2.1 2.2 2.3

Creating A DSL Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Some Modications To The Grammar Project . . . . . . . . . . . . . . . . . . . 18 Designing The Domainmodel DSL . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.3.1 2.3.2 2.3.3 The Domainmodel DSL Grammar . . . . . . . . . . . . . . . . . . . . . . 19 The Prelude . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Dening A Language Rule For Parsing An Entity Denition . . . . . . . 20

2.4 2.5

Generating The Implementation For Our DSL . . . . . . . . . . . . . . . . . . . 26 Trying Out The Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.5.1 2.5.2 Starting A Runtime Eclipse Instance . . . . . . . . . . . . . . . . . . . . 29 Creating A Test Project . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

2.6

Wrap Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 35

3 Phase 0.2 - Code Generation 3.1 3.1.1 3.1.2 3.1.3 3.2 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.3

Generator Utility Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Adding Our First Xtend Classes . . . . . . . . . . . . . . . . . . . . . . . 35 Xtend Class Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Package Name Computation . . . . . . . . . . . . . . . . . . . . . . . . . 38 Generator Class Basic Structure . . . . . . . . . . . . . . . . . . . . . . . 41 Implementation For A Java POJO Generator . . . . . . . . . . . . . . . 43 Injecting Other Xtend Classes . . . . . . . . . . . . . . . . . . . . . . . . 44 Collecting All Entity Instances And Invoke A Template Method . . . . . 45 Rich Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Generator Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Testdrive The Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

LWC11 4 Phase 0.3 - Constraint Checks 4.1 4.2

Submission 49

Name uniqueness check . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Implementing own validation rules . . . . . . . . . . . . . . . . . . . . . . . . . . 51 55

5 Phase 0.4 - Splitting A Model Into Several Parts 5.1 5.2

Splitting The Test Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 How does this work? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 58

6 Phase 1 - Advanced 6.1 6.2

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Extending The Domainmodel DSL . . . . . . . . . . . . . . . . . . . . . . . . . 58 6.2.1 6.2.2 Deriving The Domainmodel DSL From Xbase . . . . . . . . . . . . . . . 59 Workow Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

6.3

Resolving Compile Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 63

7 Phase 1.1 - Show The Integration Of Several Languages 7.1 7.2 7.3 7.4 7.5

Instance DSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Create The Instance DSL Projects . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Xbase Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Dening The Grammar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Metamodel Renement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 Move Ecore And Genmodel File To Model Directory . . . . . . . . . . . 69 Change ObjectLiteral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Change EMF Generator Target Directory . . . . . . . . . . . . . . . . . 71 Using the manually maintained Ecore model . . . . . . . . . . . . . . . . 72 Customizing the ObjectLiteral class . . . . . . . . . . . . . . . . . . . . . 73

7.6

Implementing the Scope Provider . . . . . . . . . . . . . . . . . . . . . . . . . . 76 77

8 Phase 1.2 - Implement Runtime Type Systems 8.1 8.1.1 8.1.2 8.1.3 8.2 8.2.1 8.2.2

Domainmodel DSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 JVM Model Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Detailed Description Of The Transformation . . . . . . . . . . . . . . . . 81 Testing the Domainmodel DSL . . . . . . . . . . . . . . . . . . . . . . . 83 Type Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Testing The Instance DSL . . . . . . . . . . . . . . . . . . . . . . . . . . 87 88

Instance DSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

9 Phase 1.3 - How To Do A Model-To-Model Transformation 9.1

Creating The Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

LWC11 9.2 9.3 9.4

Submission

Creating The Ecore Target Metamodel . . . . . . . . . . . . . . . . . . . . . . . 90 Implementing The Xtend Transformation Class . . . . . . . . . . . . . . . . . . 93 Creating The Transformation Workow . . . . . . . . . . . . . . . . . . . . . . . 96 9.4.1 9.4.2 9.4.3 Custom MWE2 Workow Component . . . . . . . . . . . . . . . . . . . 96 Implementing The Workow . . . . . . . . . . . . . . . . . . . . . . . . . 97 Package Export . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Add Runtime Dependencies For The Test Project . . . . . . . . . . . . . 100 Create Launch Conguration . . . . . . . . . . . . . . . . . . . . . . . . 100 Executing The Transformation . . . . . . . . . . . . . . . . . . . . . . . . 102 104

9.5

Testing The Transformation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 9.5.1 9.5.2 9.5.3

10 Phase 1.4 - Introducing A Namespace Concept

10.1 Grammar Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 10.2 Generator Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 10.3 JVM Model Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 10.4 Testing Package Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 11 Phase 1.5 - Integrating Manually Written Code 109

11.1 Grammar Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 11.2 Runtime Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 11.2.1 JVM Model Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 11.3 Type Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 11.3.1 Simple Name Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 11.3.2 Qualied Name Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 11.3.3 Value Converter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 11.3.4 Guice Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 11.3.5 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 11.3.6 Extending The Generator . . . . . . . . . . . . . . . . . . . . . . . . . . 117 11.4 Testing The New Feature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 12 Phase 1.6 - Multiple generators 123

12.1 Extending The Code Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 12.2 Testing The Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 13 Phase 2 - Non Functional 124

13.1 Phase 2.1 - How to evolve the DSL without breaking existing models . . . . . . 124 13.1.1 Adding rename element refactoring support . . . . . . . . . . . . . . . . 124 13.2 Phase 2.2 - How to work with the models eciently in the team . . . . . . . . . 126

LWC11 -

Submission

13.3 Phase 2.3 - Demonstrate Scalability Of The Tools . . . . . . . . . . . . . . . . . 127 13.3.1 Scalability of Xtext DSL UI . . . . . . . . . . . . . . . . . . . . . . . . . 127 14 Phase 3 - Freestyle 129

14.1 Creating A DSL Text Formatter . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 14.2 Headless Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 14.2.1 Create The Main Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 14.2.2 Create A Launch Conguration For The Main Class . . . . . . . . . . . . 132 14.2.3 Export A Runnable Jar . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 14.2.4 Executing The Generator . . . . . . . . . . . . . . . . . . . . . . . . . . 133 14.3 Creating A Code Generator For The Instance DSL . . . . . . . . . . . . . . . . 134 14.3.1 ExpressionsCompiler.xtend . . . . . . . . . . . . . . . . . . . . . . . . . . 134 14.3.2 InstancesGenerator.xtend . . . . . . . . . . . . . . . . . . . . . . . . . . 135 14.4 Tweaking The User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 14.4.1 Customizing The Outline View . . . . . . . . . . . . . . . . . . . . . . . 139 14.4.2 Quickxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

LWC11 -

Submission

1 Introduction
The tasks to solve for the Language Workbench Competition is relatively trivial. But it has been designed to illustrate as many properties of language workbenches as possible. As such, it can serve as a good introduction to the respective tools. This tutorial expects that you are somehow familiar with Java and Eclipse and have heard about EMF and how it works in general before. We start almost at the beginning, but not quite :-) For this tutorial we will use Xtext 2.0, which is at the moment of writing not nalized, but in its latest stage. Xtext 2.0 will be released with Eclipse Indigo in June 20117 . All tasks could also be solved with Xtext 1.0, but for some of the advanced tasks Xtext 2.0 will have better support and dierent approaches. All examples here will already work with the current builds of Xtext 2.0 and release will be soon after the rst publication of LWC11.

1.1 Task Description


The LWC11 task is divided into the following phases, and the structure of this document follows them:

1.1.1 Phase 0 - Basics 0.1 Simple (structural) DSL without any fancy expression language or such. Build a simple data denition language to dene entities with properties. Properties have a name and a type. It should be possible to use primitive types for properties, as well as other Entities. 0.2 Code generation to GPL such as Java, C#, C++ or XML. Generate Java Beans (or some equivalent data structure in C#, Scala, etc.) with setters, getters and elds for the properties. 0.3 Simple constraint checks such as name-uniqueness For example, check the name uniqueness of the properties in the entities. 0.4 Show how to break down a (large) model into several parts, while still cross-referencing between the parts.

http://wiki.eclipse.org/Indigo/Simultaneous_Release_Plan

LWC11 1.1.2 Phase 1 - Advanced

Submission

This phase demonstrates advanced features not necessarily available to the same extent in every LWB. 1.1 Show the integration of several languages. Dene a second language to dene instances of the entities, including assignment of values to the properties. This should ideally really be a second language that integrates with the rst one, not just more syntax in the same grammar. We want to showcase language modularity and composition here. 1.2 Demonstrate how to implement runtime type systems. The initialization values in the instance-DSL must be of the same type as the types of the properties. 1.3 Show how to do a model-to-model transformation. Dene an ER-meta model (Database, Table, Column) and transform the entity model into an instance of this ER meta model. 1.4 Some kind of visibility/namespaces/scoping for references. Integrate namespaces/packages into the entity DSL and make sure in the instance DSL you can only assign values to the properties of the respective entity. 1.5 Integrating manually written code (again in Java, C# or C++). Integrate derived attributes to entities. Note that if you want, you can also dene or reuse an expression language that allows dening the algorithm for calculating the age directly in the model. Ideally, you will show both (manually written 3GL code as well as an expression language). 1.6 Multiple generators. Generate some kind of XML structure from the entity model.

1.1.3 Phase 2 - Non-Functional Phase 2 is intended to show a couple of non-functional properties of the LWB. The task outlined below does not elaborate on how to do this. 2.1 How to evolve the DSL without breaking existing models 2.2 How to work with the models eciently in the team 2.3 Demonstrate Scalability of the tools

1.1.4 Phase 3 - Freestyle Every LWB has its own special cool features. In phase three we want the participants to show o these features. Please make sure, though, that the features are built on top of the task described below, if possible.

LWC11 -

Submission

1.2 Xtext Overview


This overview will give you a rough idea about what Xtext8 is all about. We will then dive into the details and work on a small project. In a nutshell, Xtext is a workbench to create and work with textual domain-specic languages (DSLs). It comes as a feature (set of plugins) to the popular Eclipse IDE. The rst thing you will want to do is to create your own domain-specic language (DSL) and specify a grammar for it. The grammar le is a plain text le with .xtext lename extension, and the grammar within is dened with a BNF like syntax. While you can use any text editor to modify it, Xtext gives you a specialized editor for grammar les. It is aware of the Xtext language, gives you syntax coloring, code completion, and more. To get a rst impression see the screenshot of the Xtext grammar le, opened with the Xtext grammar editor, below. It is not required to fully understand the content yet, this will be discussed in the next chapter in detail.

An example for DSL is one that allows you to dene entities like Person, Car, Book, and so on. An entity has properties, e.g. a Person has a name, a gender, and a date of birth. A Book has a title, one or more authors, and an ISBN number. A textual DSL model could look like this, but you could also imagine other syntaxes:
8

http://www.xtext.org

10

LWC11 -

Submission

1 2 3 4 5 6 7 8 9 10 11

entity Person { name : String gender : m birthday : Date } entity Book { title: String authors: Person[] isbnNumber: String }

Note that the Property authors is of type Person, so there can be references between entities. In the Xtext grammar le you specify how you want to dene entities and their properties. Once you have completed your language, you can do that: dene some entities, say Book and Person, together with their respective properties and with proper references between them. The nice thing is that Xtext not only gives you a syntax-driven editor for editing grammar les. Additionally it generates an editor that is specic to the language you have dened. It knows about your languages keywords and where to place them, it knows about all the syntactical constructs you have made up in your grammar, it includes all the nice stu like syntax coloring, code completion, validation, and more. For example, if you are at some point where a reference to another entity must be inserted, your DSL editor shows you all the references that would be valid here according to your language rules and lets you choose among them. All in all, using the DSL editor generated by Xtext, it is quite easy to establish a text le that adhers to your DSL. Depending on your languages type, you could call this text le e.g. a model, a document, a program, or whatever. We will refer to DSL les here as models (les). Consider now that you have created a model. What can you do with it? A typical requirement is to generate an implementation of it in a language like Java, C++, or XML. Or a graphical representation. Or something quite dierent. This is where code generation comes in. Xtext creates a skeleton code generator for you. Typically you use that code generator as a starting point to produce e.g. Java source code, documentation in, say, DocBook or Wiki format, overview graphics using GraphViz, or any other stu you need. Xtext oers special support for textual output formats, but it is also possible to generate binaries. This was only a short outline of some prominent Xtext aspects. It is by far not everything Xtext can do for you, but it should suce for now. The next chapters will show you in more

11

LWC11 detail how to work with Xtext.

Submission

1.3 Installing Xtext


Xtext is a feature for the Eclipse IDE. To install it you have two options: You can download Xtext separately and install it in your Eclipse instance. You can download a specially-crafted complete Eclipse distribution which has Xtext prepackaged already. We will take the latter approach here and describe the individual steps: 1. Go to the Xtext download page. Here you can get Eclipse 3.6.2 (Helios) containing a nightly build of Xtext 2.0 along with some tools Xtext depends on. The latter are subsumed here under Xtext for simplicity. If you want you can download also a distribution which is already bundled with Eclipse 3.7.0 Indio, but be aware that this is not nalized until end of June 2011. 2. The Eclipse/Xtext distribution is available for multiple platforms. a) Linux GTK x86 64 bit b) Linux GTK x86 32 bit c) Mac OSX x86 64 bit d) Mac OSX x86 32 bit e) Windows 64 bit f) Windows 32 bit 3. Unpack the downloaded archive le in a directory of your choice. Example (Linux):
1 2 3

cd /opt/local gzip -dc /download/eclipse-SDK-3.6.2-xtext-1.0.2-linux-gtk-x86_64.tar.gz | tar xvfp -

The archive will be extracted to a new directory named eclipse. Before unpacking the archive, please ensure that there is no subdirectory named eclipse yet! Dierent operating systems may require dierent unpacking methods.9 4. Start Eclipse by running the eclipse executable in the newly-created eclipse directory.

On Windows do not unpack it into a deep directory, since this might cause troubles with long path names.

12

LWC11 -

Submission

1.4 Workspace Setup


Before we begin, start Eclipse and set up a fresh workspace. Some settings should be done. Open the workspace settings: Windows: Window / Preferences Mac: Eclipse / Preferences

Workspace Encoding File encoding is important to some type of les. It is better that the workspace is set to a common encoding to avoid any platform specic encoding. By default the workspace is using platform encoding, which is Cp1252 on Windows and MacRoman on Mac. We will use ISO8859-1 as a common encoding here. Open Eclipse Preferences and go to General / Workspace Change setting Text le encoding to Other / ISO-8859-1

Launch Operation

Open Run/Debug / Launching Change Launch Operation to Always launch the previously launched application This will allow you re-running the previous launched application by just pressing the Run or Debug button in the Eclipse toolbar, or using keyboard shortcuts. The default settings does not always do what you want.

2 Phase 0.1 - Basics


In this chapter you will learn how to create the rst implementation of a DSL which allows modelling of Entities and Properties.

13

LWC11 -

Submission

2.1 Creating A DSL Project


Once Eclipse is installed, we can use it to establish our DSL. We will align this tutorial to Xtexts Domainmodel Example project, since this basically covers these LWC11 some tasks already (0.10.4, 1.4 & 1.5), just with a slightly dierent grammar. The Domainmodel Example can be created with the New Project Wizard (see Examples section), but want to describe in detail how to create this project from scratch. To do so, we rst create an Xtext project. Please be prepared that Eclipse does not create only a single project, but rather three of them see below. Here are the steps in detail: 1. From the menu bar, select File New Project. . . 2. The New Project window opens. 3. Select Xtext Xtext Project and click on the Next button.

4. The New Xtext Project window appears. 5. Fill in the various text elds in this form.

14

LWC11 -

Submission

a) Specify the name of the project in the Project name eld. It should be in the style of a Java package name. For our example we call the project org.eclipse.xtext.example.domainmodel. b) In the Language section, specify the name of the language to be created. The language name should mimic a fully-qualied Java class name, e.g. org.eclipse.xtext.example.domainmodel.Domainmodel. c) You also have to specify a lename extension for your model les. Eclipse will open les with this extension in your DSL-specic editor by default. Choose the le extension dmodel 6. Click at the Finish button. Xtext creates some Eclipse projects for you. This will take a few moments. The result looks like this:

15

LWC11 -

Submission

The Package Explorer view on the left displays three new projects. We will look into them in a second. The editor frame in the middle of the window shows a sample DSL denition. We will use it as a start and change it to what we think our DSL should look like. The view on the right provides a structured outline of the DSL. The dierent projects now are: org.eclipse.xtext.example.domainmodel: This project contains the grammar denition and will contain the runtime part of your DSL. We will refer to this project later as the Grammar project or Runtime project. Although the runtime depends on Eclipse libraries it is fully functional without an Eclipse runtime environment, which means that it can be executed without OSGi/Equinox. org.eclipse.xtext.example.domainmodel.ui: This project contains the UI for your DSL, especially a fully-edged Eclipse based editor. The UI of a Xtext DSL is dependent on Eclipse. org.eclipse.xtext.example.domainmodel.tests: It is really important to have unit tests for the development of non trivial DSLs. This project is meant to develop tests for your runtime. However, we wont use this project in this tutorial, you can delete it. In later versions this document might be extended to show how you program unit tests

16

LWC11 against the DSL.

Submission

For now the project wizard created a small example grammar, which is for our LWC example useless. We will overwrite it soon.

17

LWC11 -

Submission

2.2 Some Modications To The Grammar Project


We will now do some small modications to the Grammar project in preparation to the upcoming tasks. This is just cosmetics and in order to make the project align more to the original Domainmodel example. Rename the GenerateDomainmodel.mwe2 le to GenerateDomainmodelLanguage.mwe2. This name ts better its purpose this le does not generate some model, it generates the implementation for our Domainmodel language. Now open the le and replace the rst line with
1

module org.eclipse.xtext.example.domainmodel.GenerateDomainmodelLanguage

Rename original .mwe2

the le

le name. Change

GenerateDomainmodel.mwe2.launch the value GenerateDomainmodel.mwe2

to to

GenerateDomainmodelLanguage.mwe2.launch and open the le, since it has a reference to the GenerateDomainmodelLanguage.mwe2 in this line:
1 2

<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="src/org/xtext/example/domainmodel/GenerateDomainmodelLanguage.mwe2"/>

18

LWC11 -

Submission

2.3 Designing The Domainmodel DSL


As we have seen in the previous section, Xtext has already created a sample DSL. In order to modify it, we could start typing right away because Xtext has opened the le. On the other hand we should know where it is located, just in case. Well, it resides in the project org.eclipse.xtext.example.domainmodel project as le src/org/eclipse/example/domainmodel/Domainmodel.xtext.

2.3.1 The Domainmodel DSL Grammar For our Domainmodel DSL we edit the grammar le and replace its contents by the following text10 :
1

grammar org.eclipse.xtext.example.domainmodel.Domainmodel with org.eclipse.xtext.common. Terminals

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

generate domainmodel "http://www.xtext.org/example/Domainmodel" DomainModel: elements+=Type*; Type: Entity | Datatype; Datatype: "datatype" name=ID; Entity: "entity" name=ID ("extends" superType=[Entity])? "{" features+=Feature* "}"; Feature: Property; Property: name=ID ":" type=[Type];

Okay, lets look at this in some detail to understand what we are doing here.
10

http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ Domainmodel.xtext.snippet1

19

LWC11 2.3.2 The Prelude

Submission

The rst lines are some sort of prelude. If you want, you can take it as given at this point and dont bother with it any more. Or you can read the next two paragraphs to learn a little bit more about it.
1 2

grammar org.eclipse.xtext.example.domainmodel.Domainmodel with org.eclipse.xtext.common.Terminals

The grammar statement species the grammars name. The second part of the line, with org.eclipse.xtext.common.Terminals, states that we want to derive from another grammar, in this case org.eclipse.xtext.common.Terminals. 11 This is a library grammar shipped with Xtext. It contains the most common terminal symbols, such as ID, STRING, or INT. It also introduces Java-style single-line and multi-line comments as well as rules for whitespace, which may occur everywhere by default. You can open the denition of the Terminals base grammar by put the cursor there and press F3. Now lets come to the next line:
1

generate domainmodel "http://www.xtext.org/example/Domainmodel"

The generate statement tells Xtext to take our DSL and generate an Ecore EPackage from it with the specied name. If you dont know (yet), what Ecore is, dont bother with it for now, just read on! Xtext can derive the metamodel for you, and this is the easiest way to start. However, it is also possible to use an existing Ecore metamodel and map an Xtext grammar to it. We will not examine this option further now, since we are totally ne with what Xtext will derive from our grammar12 . Okay, that was the prelude, and now we can dive into the specics of our language. For didactic reasons we wont follow the grammar le line by line but rather look at its elements in a dierent sequence.

2.3.3 Dening A Language Rule For Parsing An Entity Denition The primary concept of our DSL is the Entity, which should be introduced by the keyword entity. The following example shows how we would like to dene an entity Person.
11

Xtext allows only single inheritance for grammars, but of course you can derive from a grammar which derives from another one. We will practice that later in this tutorial

12

20

LWC11 A Sample Entity:


entity Person { name : String firstname : String birthdate : Date father: Person mother: Person }

Submission

1 2 3 4 5 6 7

This denition consists of ve parts: the keyword entity the entitys name, an opening brace {, a couple of Property denitions, and a closing brace } This structure we have to formalize as a rule in the grammar le. It looks like this:
1 2 3 4

Entity: entity name=ID (extends superType=[Entity])? { features+=Feature* };

This rule describes how an entity should look like by dening the nonterminal symbol Entity. It starts with the symbol name Entity, followed by a colon, followed by what makes up an Entity: the keyword entity included between quotes meaning that it must occur as a string literal in an actual entity denition, the name of the entity to be dened, e.g. Person, optional the entity can extend another one, which is referred by a cross-reference. Crossreferences to other rules are marked in [] an opening brace this is again a keyword for the language, optional one to many Feature specications (where Property is the only kind of Feature supported in the grammar at this stage), and a closing brace. A semicolon closes the rule denition. Lets have a look at the construct name=ID. We need it for two dierent things:

21

LWC11 1. We want to pin down how an entitys name should syntactically look like.

Submission

2. We want to grab the entitys name so that we can use it later. A typical usage is to establish a reference to it, like e.g. Person entity has a reference to a Car entity the owned car. Another usage is to generate source code in some programming language. Typically you will use an entitys name to e.g. generate a corresponding Java class name. First things rst: How should an entitys name look like? Lets say we dont want entity names like e?rt& or 0815 but instead stick to a letter or underscore followed by zero or more letters, digits, or underscores. Since this is a rather common syntax for names, we can use the terminal symbol ID dened in org.eclipse.xtext.common.Terminals. This saves us specifying our own rule for the entity name syntax. If you are more interested in how the ID terminal rule is dened, then you can navigate to it by selecting ID and press F3 or hold CTRL/CMD pressed and drag over ID and click the hyperlink. This will open Terminals.xtext. This is also how you can navigate to any Cross-Reference in Xtext editors.

Assigning Parsed Values To Features: The name= part of name=ID needs a bit more explanation and so we have to back up a bit. In order to be able to process les written in a DSL, Xtext creates a parser for that particular DSL. When parsing a DSL le, the parser checks it for correct syntax and transforms it into an internal data structure. This data structure is often called an abstract syntax tree (AST). If the parser encounters an entity denition in the DSL le, it creates a corresponding Entity element in the AST. And here we get back to the name=ID construct. This notation means that name becomes an attribute (more specic: An EAttribute) of the Entity type. The parsed ID or better: the string value returned by the ID terminal rule becomes the attributes value. Later a code generator or some other tool can access the Entity model element and e.g. read its name feature.

Parsing Multiple Values: While each entity has exactly one name, it may have any number of properties. In our sample grammar le we write this down as features+=Feature*. While this looks similar to name=ID, there are some important dierences (apart from the names): 1. While ID is a predened rule, Feature is not yet dened. Keep patient, we will do that soon. 2. The rule name Feature is followed by a *. This is a quantier telling the parser that it should expect zero or more occurences of the preceeding expression (i.e. Feature) in the DSL le. The Xtext language knows about some more quantiers, see the next section

22

LWC11 Expression Quantiers.

Submission

3. Since an entity can have multiple properties, the Entitys features type is a list. This is implicitly caused by the += operator. It adds something the parsers has recognized on the right side (here: a Feature) to what is already there on the left side (here: the Feature elements already parsed for the current Entity). 4. Since Feature is another type in the created DSL metamodel, features is a reference (Ecore: EReference) instead of an attribute.

Expression Quantiers: And here is a complete list of the quantiers dened in the Xtext language: 1. expr: no quantier, the expression may occur exactly once. 2. expr?: the expression may occur once or not at all. 3. expr*: the expression may occur zero or more times. 4. expr+: the expression may occur one or more times.

Dening The Property Rule: We have seen that the Entity rule uses another rule named Feature. This rule is dened as follows:
1 2

Feature: Property;

This is just an indirection and it calls the Property rule13 :


1 2

Property: name=ID : type=[Type];

That means that a property denition consist of a name and a type. The Type rule denes what a type is resp. how it syntactically looks like. However, dening types inside Property denitions is not a good idea. Instead we merely want to use a Type that is dened elsewhere. This is what the [Type] notation does: It references an object in the abstract syntax tree that is a Type and has the given name14 . Lets explain this by an example. Consider this property denition:
1

firstname : String
13

This indirection is not needed yet, but later we want to introduce also Operations into the Entity rule and then we want to make Feature the common base type of Property and Operation We call that cross-references

14

23

LWC11 -

Submission

To have String match the Type rule, there must be a Type named String dened somewhere else in the model. Of course it is possible to refer with the cross-reference notation [...] to the same element in which the cross-reference is declared. Lets say we want to extend the Person example from section A Sample Entity by a persons parents. We can write this simply as additional properties father and mother of type Person:
1 2 3 4 5

entity Person { ... father: Person mother: Person }

The Type Rule: In section Dening The Property Rule we assumed that there was a Type rule and we have used it as an example for references to model elements. Now is the time to deal with the Type rule itself. Before looking at how Type is dened, one or two additional thoughts. Entities use types as a means to characterize their properties. In principle we have two kinds of types: Entities: An entity has an internal structure and consists of a number of properties. Each property has a type which can in turn be an entity or a simple datatype. Data types: A data type is simple and not subdivided any further. The concepts of String, Integer, or Date should suce as examples. Now that we know that a type is either an (complex) entity or a (simple) data type, we can write down the Type denition:
1 2

Type: Entity | Datatype;

The | is an alternative and eectively means that Type will be an abstract base type for Entity and Datatype. We still need the Datatype rule. Here it is:
1 2

Datatype: datatype name=ID;

24

LWC11 -

Submission

A data type denition simply consists of the keyword datatype followed by the data types name (String, Boolean, etc.). We do not have any built-in types dened. All used datatypes in our language are ment to be declared explicitly by the datatype rule.

Wrapping It Up - The Model Rule: The rst rule in the grammar le species the model element that has to be in a DSL le conforming with the grammar. In most cases it is useful to have a single model element in a DSL le. For our purposes we therefore dene this DomainModel rule:
1 2

DomainModel: elements+=Type*;

With this rule our model is complete and we can save the le Domainmodel.xtext to the project root.

15

. In this

notation the rst path component denotes the Eclipse project, the rest is the le path relative

15

/org.eclipse.xtext.example.domainmodel/src/org/eclipse/example/domainmodel/Domainmodel.xtext

25

LWC11 -

Submission

2.4 Generating The Implementation For Our DSL


So far the projects are nearly empty. We have just dened the grammar, and from this Xtext will derive the implementation with its code generator. Heres how to generate the implementation for our language: 1. In the Eclipse Package Explorer view, right-click on
16

GenerateDomainmodelLanguage.mwe2.launch. The context menu opens. 2. In the context menu, select Run As GenerateDomainmodelLanguage.mwe2 . 3. Depending on your Xtext installation you might get a prompt in your Console window, asking whether to download the le http://download.itemis.com/antlr-generator-3. 2.0.jar. Enter y in the console and press return.

Xtext will now produce the implementation code for the Xtext grammar and conguration. The Xtext code generator is modular and uses so-called generator fragments to produce specic parts of the implementation
17

. Which fragments are used and how they are congured can be seen

in the GenerateDomainmodelLangauge.mwe2 le. For now we can live with the defaults. As a result previously created projects are lled with generated code. The projects look now like this:

16 17

If Eclipse complains about errors in required projects, click on the Proceed button. Internally it uses Xpand as code generator framework

26

LWC11 -

Submission

Xtext follows the Generation-Gap Pattern, which basically means that generated and manually written code is completely separated and generated code is produced against a framework (here Xtext).

(source: http://heikobehrens.net/2009/04/23/generation-gap-pattern/)

27

LWC11 -

Submission

Files that are regenerated every time you run the code generator will be placed in the src-gen folder of the projects, and les that are meant to be manually maintained are in the src folder. On rst time execution of the code generator Xtext produced some les into the src folder also, but they wont be touched with further executions of the generator. You can safely extend them and add other classes. IMPORTANT: This generator process must be executed again whenever the grammar or the workow conguration changes. We will remind you when this step should be done again in the tutorial, but just keep this in mind.

28

LWC11 -

Submission

2.5 Trying Out The Language


2.5.1 Starting A Runtime Eclipse Instance The easiest way, and preferred way during development, to test the DSL tooling is to start a Runtime Eclipse instance, which means that we start a new Eclipse instance from within our running instance into which all plugins (our projects are Eclipse plugin projects) are deployed. To do so open the Run Congurations dialog from the Run menu. Select the entry Eclipse Application and create a new launch conguration using the left-most icon with the plus-sign. A new launch conguration is created then. Enter the Name LWC Runtime and Location ${workspace_loc}/../lwc11-runtime. Of course this step is completely optional.

Now

switch

to

the

Arguments

tab.

In

the

VM

arguments

text

area

add

-Xmx512m -XX:MaxPermSize=192m. This is for safety, depending on your Eclipse installation you might run out of memory from the Perm Gen space area.

29

LWC11 -

Submission

The other options besides the memory settings (Xmx, Xms, MaxPermSize) depend on your OS and your Eclipse startup conguration, so dont bother if the settings in the dialog appear slightly dierent for you. Now press the Run button and the Runtime Eclipse instance will start up.

2.5.2 Creating A Test Project The fresh runtime Eclipse instance will start up with an empty workspace, located in the directory lwc11-runtime next to your development workspace. Now create a new Java Project with File / New / Java Project and name it lwc11-test. In the src folder create a new le (File / New / File) with name DomainModel.dmodel. Xtext will now recognize that a le with the extension .dmodel is an Xtext resource and will ask you whether you want to add the Xtext nature for the project.

30

LWC11 -

Submission

This enables Xtext tooling for your project, so answer with yes. Put the following code into the le
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 18

datatype String datatype Date entity Person { name : String firstname : String birthdate : Date ownedCar : Car father: Person mother: Person } /** * Think about some meaningful documentation here... */ entity Car { make : String model : String }

This will look in the editor like this:

18

http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase0/DomainModel.dmodel

31

LWC11 -

Submission

You will recognize that the editor already recognizes the keywords and highlightens them. On the right side the Outline view presents the structure of the model. Left to the text you can see small buttons with a minus sign. If you have added comments to elements and drag over an element of that type a tooltip will pop up and shows the documentation. And of course we can customize all this behavior if we want to! Play a bit around with the editor. See how it behaves when making syntactical or semantic errors, add new properties, etc. One very nice feature that it will oer you is content assist. Press CTRL+SPACE at any place and you will get proposals what you can enter here! It can even oer you quick xes. For example, remove the datatype Date line. This will mark the birthdates type. If you drag over Date there the editor will propose to change the type to known types.

32

LWC11 -

Submission

33

LWC11 -

Submission

2.6 Wrap Up
This has been quite a couple of pages in this document so far, but rethink what we actually did so far: Create the Xtext project with the wizard Dened the DSL Grammar Run the Xtext code generator Started the test environment Creating a test project with a DSL model That was all! And now you have already a full IDE with support for your DSL. Nice, isnt it? And we have just touched the surface so far. . .

34

LWC11 -

Submission

3 Phase 0.2 - Code Generation


Xtext is based on EMF and integrates seamless into this framework. When parsing DSL models, an EMF metamodel is instantiated, so at runtime there is an object graph that can be processed for code generation or interpretation. The Eclipse Modeling Project has already dierent frameworks with which you could write code generators like Xpand, Acceleo and JET. All of these frameworks could be used as well, but we want to show a new language that comes with Xtext 2.0: Xtend2. Xtend2 is supposed to be the successor to M2T Xpand, which has already a history of more than a decade. All the experience in designing code generation languages along with the Xtext tooling have been put into designing Xtend2. Its name is Xtend2, since Xtend is the name of a functional language that was developed for the still popular openArchitectureWare framework, and is now part of the M2T Xpand framework. For simplicity we will often call it just Xtend.

3.1 Generator Utility Classes


Our code generator will need some utility methods to compute the name of les, packages, qualied type names etc. Those kind of computations should be kept out of the template code - on the one hand to reduce the complexity of templates and on the other side to make the logic reusable from dierent templates.

3.1.1 Adding Our First Xtend Classes Although we could implement these utility methods also with Java, we will do this with Xtend now. In package org.eclipse.xtext.example.domainmodel create a le named

DomainmodelExtensions.xtend. Here we will place utility methods that are shared between the code generator and (later) other Xtend classes. Add this content19 :
1 2 3 4 5

package org.eclipse.xtext.example.domainmodel import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.common.types.JvmDeclaredType import org.eclipse.xtext.example.domainmodel.domainmodel.*


19

http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase02/DomainmodelExtensions.xtend

35

LWC11 -

Submission

6 7 8 9 10 11 12 13 14 15 16 17 18

class DomainmodelExtensions { def dispatch String packageName(Entity o) { entities } def dispatch String packageName (Datatype t) { switch (t.name) { case Date : java.util default: null } } }

Create

second

le

named

GeneratorExtensions.xtend

in

the

org.eclipse.xtext.example.domainmodel.generator package of the runtime project. Note that this is now the generator subpackage, since we intend to place methods here, that only our generator uses. It will extend the DomainmodelExtensions.xtend class. Paste this content into it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 20

package org.eclipse.xtext.example.domainmodel.generator import org.eclipse.xtext.example.domainmodel.DomainmodelExtensions import org.eclipse.xtext.example.domainmodel.domainmodel.* import java.util.Set class GeneratorExtensions extends DomainmodelExtensions { def String qualifiedName (Type t) { if (t.packageName.nullOrEmpty) t.name else t.packageName + . + t.name } def fileName(Entity e) { e.packageName.folderName + / + e.name + .java } def folderName(String javaPackageName) { if(javaPackageName != null) javaPackageName.replace(., /) else }
20

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase02/GeneratorExtensions.xtend

36

LWC11 }

Submission

23

The syntax looks pretty much like Java on rst sight, and actually it is compiled also to Java code21 . Look into the xtend-gen folder of the runtime project, there you will nd the Java class generated from the Xtend class. Xtend will compile a .java le to this source folder when the le is saved and has valid syntax, and Eclipse will compile it immediately. The content of the generated GeneratorExtensions.java class looks like this now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
21

package org.eclipse.xtext.example.domainmodel.generator; import org.eclipse.xtext.example.domainmodel.DomainmodelExtensions; import org.eclipse.xtext.example.domainmodel.domainmodel.Entity; import org.eclipse.xtext.example.domainmodel.domainmodel.Type; import org.eclipse.xtext.xbase.lib.ObjectExtensions; import org.eclipse.xtext.xbase.lib.StringExtensions; @SuppressWarnings("all") public class GeneratorExtensions extends DomainmodelExtensions { public String qualifiedName(final Type t) { String _xifexpression = null; String _packageName = this.packageName(t); boolean _isNullOrEmpty = StringExtensions.isNullOrEmpty(_packageName); if (_isNullOrEmpty) { String _name = t.getName(); _xifexpression = _name; } else { String _packageName_1 = this.packageName(t); String _operator_plus = StringExtensions.operator_plus(_packageName_1, "."); String _name_1 = t.getName(); String _operator_plus_1 = StringExtensions.operator_plus(_operator_plus, _name_1); _xifexpression = _operator_plus_1; } return _xifexpression; } public String fileName(final Entity e) { String _packageName = this.packageName(e); String _folderName = this.folderName(_packageName); String _operator_plus = StringExtensions.operator_plus(_folderName, "/"); String _name = e.getName(); String _operator_plus_1 = StringExtensions.operator_plus(_operator_plus, _name); String _operator_plus_2 = StringExtensions.operator_plus(_operator_plus_1, ".java"); if Project / Build Automatically is activated

37

LWC11 return _operator_plus_2; } ... }

Submission

36 37 38 39

Now lets look a bit more into the details of this Xtend class:

3.1.2 Xtend Class Declaration An Xtend class le has the le extension .xtend. The main structure is similar to Java. The rst line of an Xtend class contains the package statement, which is required to match the actual source package the le is in.
1

package org.eclipse.xtext.example.domainmodel.generator

This is followed by import statements, where Java classes are imported by their name or by using wildcards. At the moment Xtend does not support automatic import management, so adding by types by packages with usage of wildcards is sometimes the better way then adding type by type. Of course it would be cleaner if you add an import statement for each used type, but we want to keep things short here.
1

import org.eclipse.xtext.example.domainmodel.domainmodel.*

Next is the class statement, again similar to Java. Of course you can derive from a real Java class or another Xtend class with extends, but we dont need that now.
1 2 3

class GeneratorExtensions { ... }

3.1.3 Package Name Computation For our code generator we will need to compute the package names for types. And types are in our language Entities and Datatypes. Datatypes should map to Java type names. For Primitives (String, Integer etc.) we do not need a package name and want return null. Others, like Date must be mapped. Entities should go for now to a static package named entities. We will later change that when we introduce a namespace concept in Phase 1.4.

38

LWC11 Single Dispatch

Submission

We want to be able to call a function named packageName on any instance of Type. This seems trivial, but consider that Java itself only support so-called Single Dispatch. What does mean? Lets take an example:
1 2 3

import java.util.ArrayList; import java.util.List;

public class DispatchSample { 5 abstract class Type {}


4 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

class Entity extends Type {} class Datatype extends Type {} public static void main(String[] args) { new DispatchSample().executeSample(); } public void executeSample() { List<Type> types = new ArrayList<Type>(); types.add(new Entity()); types.add(new Datatype()); for (Type t : types) { doSomething(t); } } public void doSomething(Type obj) { System.out.println("doSomething(Type) called"); } public void doSomething(Entity obj) { System.out.println("doSomething(Entity) called"); } public void doSomething(Datatype obj) { System.out.println("doSomething(Datatype) called"); } }

This sample denes a type hierarchy with an abstract class Type and two derived concrete classes Entity and Datatype, just like in our example. An instance of each type is created and put into a collection. For each element the method doSomething() is called, which is dened for each type of the hierarchy.

39

LWC11 -

Submission

Which output do you expect? In fact, Java will always call the method doSomething(Type). Executing this little program results in the output:
1 2

doSomething(Type) called doSomething(Type) called

This is called Single Dispatch. You have to prove the instance for its concrete and call doSomething() with type casts to actually call the proper methods for the subtypes.

Polymorphic Dispatch Xtend supports Polymorphic Dispatch (AKA multiple dispatch). This means that a method is called depending on the runtime type of an object. Usage of Polymorphic Dispatch is signalled by the keyword dispatch. By doing this to methods with the same name, but with dierent argument types of a type hierarchy, Xtend will decide depending on the actual type of an object which of the implementations it will call. For our purpose, we want to provide one implementation of packageName() for type Entity, and one for Datatype. Note, that we dont provide an implementation for the base type Type:
1 2

def dispatch String packageName(Entity o) { ... } def dispatch String packageName(Datatype o) { ... }

Extended switch Statement The Java switch statement only accepts int or enumeration values, which is a quite high restriction. In Xtend we can use any object (e.g. strings) as keys for the case statement. This is used to do a simple mapping for datatypes to the package they are dened in. For now we want only to map Date to java.util.Date. The responsibility of the packageName function is to return just the package name part, i.e. java.util for the datatype Date:
1 2 3 4 5 6

def dispatch String packageName (Datatype t) { switch (t.name) { case Date : java.util default: null } }

Qualied Name Computation The method qualifiedName() should return for any Type instance its qualied name. This is the types package name, if any, plus its name.

40

LWC11 -

Submission

1 2 3 4 5 6

def String qualifiedName (Type t) { if (t.packageName.nullOrEmpty) t.name else t.packageName + . + t.name }

Here we make rst use of Polymorphic Dispatch. The qualifiedName() method is dened for the abstract type Type and calls packageName() dependend on the actual type of t.

File Name Methods Finally, folderName() takes a dot-separated string, as it is computed by packageName(), and replaces all dots by slash /. We need this for the computation of the le name for Entity instances.
1 2 3

def folderName(String javaPackageName) { if(javaPackageName != null) javaPackageName.replace(., /) else }

The method fileName() computes the path of the resulting Java le for the code template. Again we make use of the packageName() method:
1 2 3

fileName(Entity e) { e.packageName.folderName + / + e.name + .java }

3.2 Generator Class


We will now implement a small code generator that creates Java POJO classes, i.e. Java classes with attributes and getter/setter methods for their attributes. This perfectly ts to what we can describe with the Domainmodel Language.

3.2.1 Generator Class Basic Structure A skeleton for the template of the code generator has already been produced in the generator package in form of the Xtend class DomainmodelGenerator.xtend:

41

LWC11 -

Submission

1 2 3 4 5 6 7 8 9 10 11

package org.eclipse.xtext.example.domainmodel.generator import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IGenerator import org.eclipse.xtext.generator.IFileSystemAccess class DomainmodelGenerator implements IGenerator { override void doGenerate(Resource resource, IFileSystemAccess fsa) { //TODO implement me } }

The DomainmodelGenerator class needs to implement the org.eclipse.xtext.generator.IGenerator interface


1 2 3 4 5 6 7 22

and implement its method doGenerate():

public interface IGenerator { /** * @param input - the input for which to generate resources * @param fsa - file system access to be used to generate files */ public void doGenerate(Resource input, IFileSystemAccess fsa); }

As described in the interface input will be an EMF Resource, which contains objects for which we want to generate code. The code generator will be called later per model le, and the artifacts that are derived from elements that can be found within one resource, like Entity instances in our example. Of course they can have cross-references to elements dened in other les, but the entry point for generation of specic artifacts are elements that are looked up from within resources. Sometimes you will need to generate artifacts that span all known resources, independent of one specic resource. Then you would need to traverse to the resources ResourceSet and iterate over all resources contained within. But in the given example this is not required, so just think that we will select all Entities from a given resource and create les for each of them here. Xtext has its own implementation of the Resource interface, allowing a textual DSL model be treated like any other EMF Resource.
23

22

http://download.eclipse.org/modeling/tmf/xtext/javadoc/2.0.0/org/eclipse/xtext/generator/ IGenerator.html Internally, the DSL le extension is registered in the EMF Resource Factory Registry and mapped to Xtexts implementation of Resource.Factory. You can see this in class DomainmodelStandaloneSetupGenerated.

23

42

LWC11 -

Submission

The IFileSystemAccess 24 instance passed will allow the creation of les with generated code.

3.2.2 Implementation For A Java POJO Generator Now copy the following content into DomainmodelGenerator right after:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
24

25

, we will explain this in detail

package org.eclipse.xtext.example.domainmodel.generator import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IGenerator import org.eclipse.xtext.generator.IFileSystemAccess import org.eclipse.xtext.example.domainmodel.domainmodel.* import org.eclipse.xtext.common.types.* import java.util.* import com.google.inject.Inject import static extension org.eclipse.xtext.xtend2.lib.ResourceExtensions.* class DomainmodelGenerator implements IGenerator { @Inject extension GeneratorExtensions generatorExtensions override void doGenerate(Resource resource, IFileSystemAccess fsa) { for(entity: resource.allContentsIterable.filter(typeof(Entity))) { fsa.generateFile(entity.fileName, entity.compile) } } def compile(Entity e) { IF !(e.packageName.isNullOrEmpty) package e.packageName; ENDIF e.body() } def body(Entity e) { public class e.name e.superTypeClause(){ http://download.eclipse.org/modeling/tmf/xtext/javadoc/2.0.0/org/eclipse/xtext/generator/ IFileSystemAccess.html http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase02/DomainmodelGenerator.xtend

25

43

LWC11 FOR f : e.features feature(f) ENDFOR } } def superTypeClause(Entity e) { if(e.superType != null) extends + e.superType.name + " " else "" } def dispatch feature(Feature p) {} def dispatch feature(Property p) { private p.type.qualifiedName() p.name; public p.type.qualifiedName() getp.name.toFirstUpper() { return p.name; } public void setp.name.toFirstUpper(p.type.qualifiedName() p.name) { this.p.name = p.name; } } }

Submission

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

3.2.3 Injecting Other Xtend Classes Right before we have created the utility Xtend class GeneratorExtensions.xtend. The functions dened there should be now made available in the code generator class. This can be easily done with the @Inject keyword. Like with Guice (and this is actually used behind the scenes) @Inject will inject an instance of the GeneratorExtensions class into our class:
1

@Inject extension GeneratorExtensions generatorExtensions

In this case @Inject is followed by the keyword extension. This has a special meaning on how to handle the methods of the injected class. By using @Inject extension the methods

44

LWC11 -

Submission

dened in the injected class are extending the set of features of the class which are the rst argument type of the declared method. Remember, for example, the leName() method of GeneratorExtensions.xtend:
1 2 3

fileName(Entity e) { e.packageName.folderName + / + e.name + .java }

The rst (and only) argument type is Entity here. This means that we can call fileName() as if it were a method declared for the Entity metaclass generated by EMF. Since this extends the the interface of Entity without actually implementing a second interface or changing the implementation of Entity, we can speak here of a non-intrusive extension of the metamodel. In the following screenshot you can see that the content assist proposes fileName as a direct feature for the variable entity of type Entity:

3.2.4 Collecting All Entity Instances And Invoke A Template Method Take a look now at the implementation of the doGenerate() method:
1 2 3 4 5

override void doGenerate(Resource resource, IFileSystemAccess fsa) { for(entity: resource.allContentsIterable.filter(typeof(Entity))) { fsa.generateFile(entity.fileName, entity.compile) } }

45

LWC11 -

Submission

The expression in the for loop will collect all instances from the resource that are of type Entity, no matter how deep they are in the containment hierarchy of the model. For each Entity instance the method IFileSystemAccess#generateFile() will be called:
1 2 3 4 5

public interface IFileSystemAccess { ... public void generateFile(String fileName, CharSequence contents); ... }

The rst argument is the le name (including package path) for an Entity, which is computed by the extension function fileName(). Remember, fileName() was implemented in GeneratorExtensions.xtend. The second argument will be the actual le content. This will be evaluated by calling the compile() method:
1

compile(Entity e) { ... }

3.2.5 Rich Strings Code generation is more or less just building of strings. There are two main approaches that code generation frameworks: Template based and Visitor based. In practice, template based approaches often scale better for large-scale projects. Not because of their performance, more because they are easier to understand, and thus better to communicate and maintain. Xtend oers such a template based approach, which is called Rich Strings. Unlike its predecessor, Xpand, and most other frameworks, there is no gap between template language and integration of generator logic code. All is done with Xtend and the expression language behind, Xbase, with full integration of the Java language. Rich Strings are introduced by 3 single quotes . Everything within this rich string is appended to the result. There is some support for the typical stu that you need during code generation, like conditions and loops, which are escaped with guillemot brackets . Before you Google how the heck you nd those brackets on your keyboard, there is easy access to them: The Xtend editor binds these characters to CTRL+< and CTRL+>. Now let us examine the compile() method:
1 2 3

compile(Entity e) { IF !(e.packageName.isNullOrEmpty)

46

LWC11 package e.packageName; ENDIF e.body() }

Submission

4 5 6 7 8 9

The rst statement in the generated POJO le is the package statement. We have to prove if an Entity has a non-null package name, otherwise no package statement should be produced. The rest of the POJO le is evaluated in the body() method:
1 2 3 4 5 6 7 8 9

body(Entity e) { public class e.name e.superTypeClause(){ FOR f : e.features feature(f) ENDFOR } }

This will create the class statement and the name of the current Entity. If the Entity extends another one an extends clause will be inserted. This logic is done in the superTypeClause() method, which we do not explain further now. In the class body for each dened Property an instance variable and accessor methods should be created. Therefore we use the FOR loop and invoke the feature() function for each element. This function is declared twice, once for the abstract type Feature, and once for the type Property.
1 2 3 4

def dispatch feature(Feature p) {} def dispatch feature(Property p) { ... }

The rst one is required since the type of the features reference is Feature and the Xtend compiler must match the feature function also for the supertype. At the moment the only concrete subtype of Feature is Property, so actually at runtime only the function feature(Property p) will be called.

47

LWC11 -

Submission

3.3 Testdrive The Generator


We now want to give our generator a testdrive. Execution is pretty simple: Xtext will execute the generator automatically if the project containing .dmodel les contains a folder src-gen. Restart your Runtime Eclipse instance and create a source folder26 in your lwc11-test project with name src-gen now. You should get immediate the result les, e.g Car.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public class Car { private java.lang.String make; public java.lang.String getMake() { return make; } public void setMake(java.lang.String make) { this.make = make; } private java.lang.String model; public java.lang.String getModel() { return model; } public void setModel(java.lang.String model) { this.model = model; } }

26

File / New / Source Folder

48

LWC11 -

Submission

4 Phase 0.3 - Constraint Checks


Validation is an important aspect when developing a language. Feedback about erroneous les should be reported as fast as possible, best when typing. Without any customization Xtext does already validate models for synactical correctness, resolvability of cross-references (i.e. linking) and the concrete syntax.27 Since Xtext models are EMF models internally Xtext DSL les can be validated by any means EMF models support for validation. This can be OCL, Xtend, Java etc. All is based on the EMF validation framework.
27

see http://tinyurl.com/6d54ooe

49

LWC11 -

Submission

Xtext ships with its own integration to the validation framework, which uses Java annotations. For each DSL Xtext generates a stub class, which is registered to the Runtime Module28 of the DSL. In our case, this class is named DomainmodelJavaValidator and can be found in the validation package in the DSL runtime project.

4.1 Name uniqueness check


Name uniqueness is assured automatically. If you, for instance, copy/paste an Entitys property, you will get an error marker for both lines that contain the feature with similar name.

Xtext supports dirty-state aware editing29 . For validation this means if you correct an error it will be marked as resolved (the red cross turns grey) before saving. On saving also the problem marker in the Problems View will be removed.

28 29

see http://tinyurl.com/6y5mqq4 see http://blog.efftinge.de/2010/01/xtexts-new-builder-infrastructure.html for a screencast demonstrating this feature

50

LWC11 -

Submission

4.2 Implementing own validation rules


For each validation rule to evaluate a method annotated with @Check is added. The name of the method is just for information. A validation method takes an instance of a type from the DSLs metamodel.
1 2 3 4 5 6 7

public class DomainmodelJavaValidator extends AbstractDomainmodelJavaValidator { @Check public void myValidationMethod (Entity ctx) { // add implementation here } }

The following validation rules should be implemented now: 1. The name for Entity instances should start with a capital character. Violations should be reported as warnings. 2. The name for Property instances should start with a lower case character. Violations should be reported as warnings. 3. Detection of circular inheritance. This will be an error.

Validation method signature The base class AbstractDeclarativeValidator provides convenience methods to report warnings (warning(args)) and errors (error(args)). The methods exist with dierent signatures, which take a dierent set of the following arguments: 1. String message: Issue message text 2. EStructuralFeature feature: The value for the feature is taken from the metamodels generated EPackage interface (DomainmodelPackage.Literals.XXX).

51

LWC11 3. EObject source: The validated EObject instance. 4. int index: Index of the element if validating
30

Submission

list

of

objects.

Use ValidationMessageAcceptor. INSIGNIFICANT_INDEX implement Quick Fixes.31

if the index is not relevant.

5. String code: A unique String key for the error code. This is important if you want to 6. Object... data: Any data that should be attached to the issue about the current error context (also used for Quick Fixes). In a simple case the error() and warning() methods take a message text, the current validated object and the objects feature that has been validated (e.g. the name feature of an Entity or the type feature of a Property).

IssueCodes interface We will create an interface which declares string constants for each possible error situation. In the package org.eclipse.xtext.example.domainmodel.validation add an interface IssueCodes.java
1 2 3 4 5 6 7 8 9 32

with the following content:

package org.eclipse.xtext.example.domainmodel.validation; public interface IssueCodes { String PREFIX = "org.eclipse.xtext.example.domainmodel.domainmodel."; String INVALID_TYPE_NAME = PREFIX + "InvalidTypeName"; String INVALID_FEATURE_NAME = PREFIX + "InvalidFeatureName"; String MISSING_TYPE = PREFIX + "MissingType"; String CIRCULAR_INHERITANCE = PREFIX + "CircularInheritance"; }

Implementing validation rules to check naming conventions Open DomainmodelJavaValidator.java and replace the content with
1 2 3 4 5 33

package org.eclipse.xtext.example.domainmodel.validation; import java.util.Set; import org.eclipse.xtext.example.domainmodel.domainmodel.DomainmodelPackage; import org.eclipse.xtext.example.domainmodel.domainmodel.Entity;


30

value: -1 see http://tinyurl.com/64gwqy5 32 http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/


31 33

phase03/IssueCodes.java http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase03/DomainmodelJavaValidator.java

52

LWC11 import org.eclipse.xtext.example.domainmodel.domainmodel.Property; import org.eclipse.xtext.validation.Check; import org.eclipse.xtext.validation.ValidationMessageAcceptor; import com.google.common.collect.Sets;

Submission

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

public class DomainmodelJavaValidator extends AbstractDomainmodelJavaValidator { @Check public void checkTypeNameStartsWithCapital(Entity entity) { if (!Character.isUpperCase(entity.getName().charAt(0))) { warning("Name should start with a capital", DomainmodelPackage.Literals.TYPE__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.INVALID_TYPE_NAME, entity.getName()); } } @Check public void checkFeatureNameStartsWithLowercase(Property feature) { if (!Character.isLowerCase(feature.getName().charAt(0))) { warning("Name should start with a lowercase", DomainmodelPackage.Literals.PROPERTY__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.INVALID_FEATURE_NAME, feature.getName()); } } }

You see that the methods are annotated with @Check and have Entity or Property as argument type. With these objects it is easy to access the objects properties (here: the name property) and validate this. The most code here is just about creating the warnings with the appropriate information. The additional information besides the message itself is necessary to create the error markers at the right location in the text with the right context information. After adding these methods the runtime workbench has to be restarted. Now test the validation rules by naming Entities with a name starting with a lower case character or a Property with an upper case letter.

53

LWC11 -

Submission

Detection of circular dependencies We want to disallow circular inheritance now. The following validation method 34 goes through an Entitys inheritance hierarchy and proves if the Entity is deriving from itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
34

public class DomainmodelJavaValidator extends AbstractDomainmodelJavaValidator { ... /** * Validation: Entities must not have circular inheritances. */ @Check public void validateInheritanceHierarchy(Entity ctx) { Set<Entity> visited = Sets.newHashSet(); Entity current = ctx; while (current != null) { if (visited.contains(current)) { error("Circular inheritance detected", DomainmodelPackage.Literals.ENTITY__SUPER_TYPE); http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase03/DomainmodelJavaValidator.java.snippet

54

LWC11 break; } // remember the visited Entity visited.add(current); // Set current to next in hierarchy. Might be null, which terminates // the loop current = current.getSuperType(); } } }

Submission

15 16 17 18 19 20 21 22 23 24

With this validation rule this scenario will be detected as error:

5 Phase 0.4 - Splitting A Model Into Several Parts


In this task it should be shown how the model le could be splitted into several parts, where cross references must still work between the parts. With Xtext this works just out-of-the-box.

5.1 Splitting The Test Model


We will test this by splitting our model into 3 les: 1. Datatypes.dmodel
1 2

datatype String datatype Date

2. Person.dmodel
1

entity Person {

55

LWC11 name : String firstname : String birthdate : Date ownedCar : Car father: Person mother: Person }

Submission

2 3 4 5 6 7 8

3. Car.dmodel
1 2 3 4

/** * Think about some meaningful documentation here... */

entity Car { 5 make : String


6 7

model : String }

Dont forget to delete DomainModel.dmodel, since the elements in there potentially collide now with our new models. You will observe that everything builds again and cross-referencing still works. Also Hyperlinking between dierent model les works, even if the target model le is closed. If you hold the CTRL (Mac: CMD) key pressed while dragging over Car in Person.dmodel a hyperlink appears and you can navigate to the Car entity denition.

56

LWC11 -

Submission

5.2 How does this work?


Xtext maintains an Index of all exported objects in Xtext resources. An object gets exported to the Index if a qualied name can be computed for it. By default the DefaultDeclarativeQualifiedNameProvider is used, which will try to resolve a name feature from an objects EClass. Therefore it is important to assign a name feature to the relevant rules in the Xtext grammar, like for the Datatype rule:
1 2

Datatype: datatype name=ID;

The DefaultDeclarativeQualifiedNameProvider will then go the objects eContainer and try to do the same for the container and concatenate the names dot-seperated until it reaches the models root element, which has no container. This qualied name will be stored in the Index. In our scenario, the entities are directly contained in the model object, thus the qualied names are just the simple names of the entities so far. This will change when a namespace concept is introduced in Phase 1.4

57

LWC11 -

Submission

If other objects now have cross-references they will name these qualied names. Think of Java, it is the same here, just that you would normally import types to avoid using qualied names within the code. The Index is updated while the user types and when a model is stored. Actually, there are two indexes involved: One that is maintained during typing and which is aware of any dirty state (means state of les that were not saved so far), and one that represents the persisted state.

6 Phase 1 - Advanced
6.1 Introduction
In Phase 1 more advanced features are demonstrated. With Xtext all tasks are solvable. To implement the advanced features we will have to extend the Domainmodel DSL as preparation for the next tasks. Note that the result of our preparations will not lead to a consistent implementation so far. The following steps will require the implementation of the runtime type system for the Domainmodel DSL and the with Phase 1.1 upcoming second DSL. But the implementation of the typesystems will be done in Phase 1.2. Until nishing that phase the DSL runtime will not be functional. One of the most signicant changes with Xbase 2.0 will be Xbase, a common base language for Xtext based DSLs. Xbase adds a complete expression language that compiles down to Java. We will see this in action in Phase 1.5, where integration of the DSL with Java is demonstrated. Also it makes integration of languages easier, since integration will happen on Java level. The second DSL which is required in Phase 1.1, will be integrated with the Domainmodel Language in this way. For solving the basic tasks it is sucient to have a very simple grammar and a minimal set of basic rules, which are inherited from org.eclipse.xtext.common.Terminals. We will now change our DSL to derive from Xbase.

6.2 Extending The Domainmodel DSL

58

LWC11 6.2.1 Deriving The Domainmodel DSL From Xbase

Submission

First, we need to introduce a new dependency to the Xbase plugin. Open the META-INF/MANIFEST.MF le from the org.eclipse.xtext.example.domainmodel project, go to the Dependencies page and add org.eclipse.xtext.xbase to the Required Plug-ins section.

Next, open the grammar le Domainmodel.xtext and change it to


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

35

grammar org.eclipse.xtext.example.domainmodel.Domainmodel with org.eclipse.xtext.xbase.Xbase generate domainmodel "http://www.xtext.org/example/Domainmodel" DomainModel: elements+=AbstractElement*; AbstractElement: Entity | Import; Import: import importedNamespace=QualifiedNameWithWildCard; Entity: "entity" name=ID ("extends" superType=JvmTypeReference)? "{" features+=Feature* "}"; Feature: Property;
35

http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase10/Domainmodel.xtext

59

LWC11 -

Submission

21 22 23 24 25 26

Property: name=ID ":" type=JvmTypeReference; QualifiedNameWithWildCard : QualifiedName (. *)?;

In the rst line the with statement has been changed to derive from the Xbase grammar. The Datatype rule has been removed, since from now on Java types can be used as Property types directly. Then the cross-references to Type in the Entitys superType and Propertys type reference have be changed to JvmTypeReference, and it is not as cross-reference anymore.
1 2 3 4 5 6 7 8 9 10

Entity: "entity" name=ID ("extends" superType=JvmTypeReference)? "{" features+=Feature* "}"; Feature: Property; Property: name=ID ":" type=JvmTypeReference;

Using references to JVM types will have the consequence that we have to add runtime type information about the concepts of the Domainmodel DSL. Since this is subject of Phase 1.2, we will explain how to add this in that chapter, and this is also the reason why the implementation wont be fully working until adding the changes of Phase 1.2. The grammar supports now importing of elements by name, represented by the Import rule. Imports with wildcards (like java.util.*) are allowed by using the new datatype rule QualifiedNameWithWildCard for the importedNamespace feature. An abstract rule AbstractElement has been introduced with Entity and Import as alternatives for now, and the elements feature of the DomainModel rule has AbstractElement now as reference type.

60

LWC11 6.2.2 Workow Changes Open GenerateDomainmodelLanguage.mwe2 and uncomment the

Submission

content

of

the

EcoreGeneratorFragment. By doing this, the Xtext generated EMF classes can refer to types from the JavaVMTypes and Xbase metamodels, which is required to resolve JvmTypeReference.
1 2 3 4 5 6

fragment = ecore.EcoreGeneratorFragment { referencedGenModels = " platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel, platform:/resource/org.eclipse.xtext.common.types/model/JavaVMTypes.genmodel " }

Now run the Xtext code generator by executing GenerateDomainmodelLanguage.mwe2.launch again (Run As ...).

6.3 Resolving Compile Errors


Our latest grammar change introduced some compile problems, which must be solved now. In DomainmodelExtensions.xtend a packageName() function for the Datatype type is not needed anymore. Remove the denition def dispatch String packageName (Datatype t). Since there is only one packageName() function left now, the dispatch keyword is obsolete.
1 2 3 4 5 6 7 8 9

package org.eclipse.xtext.example.domainmodel import org.eclipse.xtext.example.domainmodel.domainmodel.* class DomainmodelExtensions { def String packageName(Entity o) { entities } }

The argument type of the qualifiedName() function in GeneratorExtensions.xtend must be changed from Type to Entity, since we do not have the abstract Type rule anymore.
1 2 3 4 5 6

def String qualifiedName (Entity t) { if (t.packageName.nullOrEmpty) t.name else t.packageName + . + t.name }

61

LWC11 -

Submission

Open DomainmodelGenerator.xtend and go to the superTypeClause() denition. The JvmTypeReference, which we now use as type for the superType feature, does not have a name feature. It is called simpleName in this class. Remember what we said about the Xtext Index? By default objects with a name feature will be put into the Index. 36 But it is not desired to have these reference objects in the Index, only the referred types themselves should be available. So to avoid exporting JvmTypeReference to the Index a dierent feature name was chosen. So change e.superType.name to e.superType.simpleName here:
1 2 3 4 5

def superTypeClause(Entity e) { if(e.superType != null) extends + e.superType.simpleName + " " else "" }

Now nally we x the DomainmodelJavaValidator class. There is one compile problem, since we refer to the TYPE__NAME feature in checkTypeNameStartsWithCapital(). This must be changed to ENTITY__NAME:
1 2 3 4 5 6

@Check public void checkTypeNameStartsWithCapital(Entity entity) { if (!Character.isUpperCase(entity.getName().charAt(0))) { warning("Name should start with a capital", DomainmodelPackage.Literals.ENTITY__NAME, ...

Also the validateInheritanceHierarchy() method must be changed to resolve the type hierarchy by the JVM types. We will use the qualied names of the types to detect inheritance cycles now. Therefore the IQualifiedNameProvider must be injected into the class
1 2 3 37 38

public class DomainmodelJavaValidator extends AbstractDomainmodelJavaValidator { @Inject private IQualifiedNameProvider qualifiedNameProvider;


36

Like many things in Xtext, the default behavior of indexing can be inuenced are exchanged. The key here is the implementation of the IQualifiedNameProvider that is bound to the Guice module of the runtime project. In a late step of this tutorial (in Phase 1.6) we will see how to extend the qualied name provider and register it to Guice. http://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase10/DomainmodelJavaValidator.java.snippet Use Source / Organize Imports to add the missing additional import statements.

37

38

62

LWC11 -

Submission

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

... @Check public void validateInheritanceHierarchy(Entity ctx) { Set<QualifiedName> visited = Sets.newHashSet(); visited.add(qualifiedNameProvider.getFullyQualifiedName(ctx)); JvmGenericType current = null; if (ctx.getSuperType()!=null) current = (JvmGenericType) ctx.getSuperType().getType(); while (current != null) { QualifiedName qfn = qualifiedNameProvider.getFullyQualifiedName(current); if (visited.contains(qfn)) { error("Circular inheritance detected", DomainmodelPackage.Literals.ENTITY__SUPER_TYPE); break; } // remember the visited Entity visited.add(qfn); // Set current to next in hierarchy. Might be null, which terminates // the loop if (!current.getSuperTypes().isEmpty()) current = (JvmGenericType) current.getSuperTypes().get(0).getType(); else current = null; } } }

So, preparations are done now, but as mentioned the Domainmodel DSL will need information about the runtime types. If you want to test the Domainmodel DSL now again, you can skip to Phase 1.2 and add the implementation now.

7 Phase 1.1 - Show The Integration Of Several Languages


In this chapter we will create another Xtext DSL which will allow to dene instances of entities. The target language should look like this:
1 2 3

Person p = { name = "Voelter" firstname = "Markus"

63

LWC11 birthdate = 14.02.1977 ownedCar = c } Car c = { make = "VW" model = "Touran" }

Submission

4 5 6 7 8 9 10 11

We will name this language Instance DSL. It will integrate with the Domainmodel Language on the Java level, which also will allow to use this language to describe the instantiation of any other Java class. You could use this language then to describe instantiation graphs for test scenarios. In Phase 3 (Freestyle) we will even create a small compiler which will translate the instantiation descriptions to Java code. As mentioned before for the Domainmodel Language, also this new language will not be fully functional at the end of the chapter. It requires adding the implementation for the runtime type system, which is subject of the next chapter.

7.1 Instance DSL


We will now create the second DSL which will allow to describe instances of Entities. Since we described the creation and structure of the projects before in rather detailed extend, we will keep things shorter now.

64

LWC11 -

Submission

7.2 Create The Instance DSL Projects


Open the Xtext project wizard again and use the following settings: 1. Project name: org.xtext.lwc.instances 2. Language name: org.xtext.lwc.instances.Instances 3. File extensions: instance

7.3 Xbase Integration


Like the Domainmodel DSL also this language will be derived from Xbase, and the same preparations must be done for the runtime project org.xtext.lwc.instances. Open the MANIFEST.MF le and add org.eclipse.xtext.xbase as required plugin. The workow diers slightly from the defaults. Open GenerateInstances.mwe2 and change the following
39

39

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/

65

LWC11 -

Submission

The Instance DSL uses rules from the Xbase and the JVM types metamodel, therefore it is necessary to refer to the genmodels of those in the EcoreGeneratorFragment. Uncomment the lines within the fragment to:
1 2 3 4 5 6

fragment = ecore.EcoreGeneratorFragment { referencedGenModels = " platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel, platform:/resource/org.eclipse.xtext.common.types/model/JavaVMTypes.genmodel " }

We do not plan to have a standalone executable code generator for the Instance DSL. By default, a Java Main class and a MWE2 workow to execute a code generator is created by the GeneratorFragment. This can be suppressed by changing it to40 :
1 2 3 4 5

// generator API fragment = generator.GeneratorFragment { generateMwe = false generateJavaMain = false }

Change the XbaseGeneratorFragment41 :


1 2 3

fragment = xbase.XbaseGeneratorFragment { generateXtendInferrer = false }

This will suppress the generation of another Xtend class that wont be used in the Instance DSL.
42

40

org/xtext/lwc/instances/GenerateInstances.mwe2 It would not harm, of course, if you leave it to default values, it is just dead code that would be produced

then. Also in MWE2 les you can invoke content assist with CTRL+SPACE 42 It would create a JVM typemodel inferrer class, which is required when implementing Xtend based co41

de generators for DSLs that derive from Xbase. The Domainmodel DSL has this (yet empty) class (DomainmodelJvmModelInferrer), and for this DSL it must be implemented, which is done in the next chapter.

66

LWC11 -

Submission

7.4 Dening The Grammar


Open the grammar le Instances.xtext and add this content
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 43

grammar org.xtext.lwc.instances.Instances with org.eclipse.xtext.xbase.Xbase generate instances "platform:/resource/org.xtext.lwc.instances/model/Instances.ecore" import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase

Instance : {Instance} imports+=Import* expressions+=XVariableDeclaration*; Import: import importedNamespace=QualifiedNameWithWildCard ; QualifiedNameWithWildCard: QualifiedName .*? ; XIntLiteral returns xbase::XExpression: {DateLiteral } =>day=INT . month=INT . year=INT | {xbase::XIntLiteral} value=INT ; XVariableDeclaration returns xbase::XExpression: {xbase::XVariableDeclaration} type=JvmTypeReference name=ID = right=XExpression ; XBlockExpression returns xbase::XExpression: {ObjectLiteral} { expressions+=XExpression* } ;

43

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/Instances.xtext

67

LWC11 -

Submission

Now execute the workow to create the initial implementation of the DSL by running GenerateInstances.mwe2.launch. As you can read this language will reuse some concepts from Xbase. Xbase allows us to create complex expressions, instantiate objects, dene variables and so on. And as mentioned it compiles down and integrates with Java. The Instance DSL will allow to describe instances of any Java class, and we will extend the Domainmodel in Phase 1.2 to behave as if Entity instances were actually declared Java types. Lets analyze the grammar in detail:

68

LWC11 -

Submission

7.5 Metamodel Renement


The Xtext generated metamodel is good, but Xtext cannot infer an optimal metamodel. We need to tweak it a bit in a way which cannot be inferred by Xtext, but therefore we need to manually edit it and let Xtext use this manually maintained metamodel. In this case we want to change the super types of the ObjectLiteral EClass and override a default implementation of the generated EMF class without overriding it within the generated implementation class.

7.5.1 Move Ecore And Genmodel File To Model Directory Create a folder named model at the root of the org.xtext.lwc.instances project. Move the generated Instances.ecore and Instances.genmodel les from src-gen to the model directory. Unfortunately the generated les contain hard references to their old location in src-gen, so to x that some text replacements must be done. Select the model folder and open the Search / File dialog. Enter the following values and press Replace to open the replace window. Search for the text src-gen/org/xtext/lwc/instances and replace it by model:

69

LWC11 -

Submission

7.5.2 Change ObjectLiteral Open the Instances.ecore


44

le with the Ecore editor

45

, select the ObjectLiteral EClass

and open the Properties View. Change the ESuper Types value: 1. Remove XExpression 2. Add JvmIdentifiableElement 3. Add XBlockExpression Open the ObjectLiteral EClass, select the expressions feature and delete it.

This Instances.ecore metamodel should look now like this:

44

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/ model/Instances.ecore Actually there are multiple Editors for Ecore models available. The screenshots were made with the Sample Ecore Model Editor, version 2.7.0. Depending on the editor you use and its version this might look slightly dierent for you. You can use another editor as the default one by using Open With from the context menu of the .ecore le.

45

70

LWC11 -

Submission

7.5.3 Change EMF Generator Target Directory With each run of the MWE workow the generator cleans the src-gen folder and thus erases also the generated EMF classes. This was ne as long as the EMF classes were also generated in this workow. Now we decoupled this, and the generation of the EMF classes is a seperate step. We want to keep the les even when the Xtext generator run, since we do not want to repeat the generation of the EMF classes each time after this. The EMF classes should go to a seperate source directory. Select the root of the project, choose File / New / Source Folder from the context menu and enter emf-gen as the folder name. Open the Instance.genmodel 46 le47 , select the root note and go to the Properties View. In the Model section you will nd an entry Model Directory. Change the value to /org.xtext.lwc.instances/emf-gen.

Select the root node from the Genmodel again and select Generate Model Code from the context
46

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/ model/Instances.genmodel If you get an error here the text replacement was not correct or the expressions feature was not removed from ObjectLiteral

47

71

LWC11 -

Submission

menu. This will generate now the les into the emf-gen source folder. For now this will result in compile errors, since the classes already exist in src-gen. Dont worry, this will be solved with our next step.

7.5.4 Using the manually maintained Ecore model To use the Instances.ecore metamodel the grammar and workow must be changed slightly. Open the GenerateInstances.mwe2 le and remove the EcoreGeneratorFragment entry. It is not needed anymore, since Xtext should not infer an Ecore metamodel. Right at the beginning, between the StandaloneSetup and the rst DirectoryCleaner, insert:
1 2 3 4 5 6 7 8

component = DirectoryCleaner { directory="${runtimeProject}/emf-gen" } component = org.eclipse.emf.mwe2.ecore.EcoreGenerator { genModel = "platform:/resource/${projectName}/model/Instances.genmodel" srcPath ="platform:/resource/${projectName}/src" }

This will invoke Xtexts specialized Ecore generator. This component will execute the Ecore generation for the manual maintained Ecore model, and extends the default behavior of EMFs Ecore generator. We will come to this back soon. Open Instances.xtext and change the second line to:
1

import "platform:/resource/org.xtext.lwc.instances/model/Instances.ecore"

Now run the workow again. The directories will be cleaned and the EMF classes are no longer generated. The compile errors will be solved now. Since Xtext does not create the Ecore model anymore, it also does not register it into the EPackage Registry for standalone execution. This has to be done manually in the InstancesStandaloneSetup class. Open the class and add this method
1 2 3 4
48

48

@Override public void register(Injector injector) { EPackage.Registry.INSTANCE.put(InstancesPackage.eNS_URI, InstancesPackage.eINSTANCE); https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/InstancesStandaloneSetup.java

72

LWC11 }

Submission

Also the InstancesJavaValidator needs to be changed. The class must return the EPackages to which the validation applies in method getEPackages(), and when using the EcoreGeneratorFragment the abstract base class does already return it. Now the method must be overloaded. Open InstancesJavaValidator and replace it with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 49

package org.xtext.lwc.instances.validation; import java.util.List; import org.eclipse.emf.ecore.EPackage; import org.xtext.lwc.instances.instances.InstancesPackage; public class InstancesJavaValidator extends AbstractInstancesJavaValidator { @Override protected List<EPackage> getEPackages() { List<EPackage> result = super.getEPackages(); result.add(InstancesPackage.eINSTANCE); return result; } }

This method is called by the Xtext framework to register the validator for the given EPackage(s) to the EMF Validation Framework.

7.5.5 Customizing the ObjectLiteral class The ObjectLiteral class needs to override the inherited getSimpleName() method. The method should return this. This is because ObjectLiteral is used as the type for block statement that initializes an instance, and within the block statement expressions should not need to qualiy anything to refer to the block itself. Take this example:
1 2 3

make = "VW" model = "Touran" }

And remember the grammar denition for block statement:


49

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/validation/InstancesJavaValidator.java

73

LWC11 -

Submission

1 2 3 4 5 6

XBlockExpression returns xbase::XExpression: {ObjectLiteral} { expressions+=XExpression* } ;

The {ObjectLiteral} action assures that the runtime type for the block will be ObjectLiteral, since the returns statement just declares to return the super type XExpression. The default implementation for simpleName() from JvmIdentifiableElementImpl just throws an UnsupportedOperationException, since the base implementation cannot decide how to compute a name for an abstract identiable element
50

. Usually one would customize an EMF

class directly in the generated class, but this is a bad practice We wont discuss this in detail here. If you read more about the Generation Gap Pattern, which is an accepted best practice in MDSD, you will nd also information about the drawbacks of mixing generated and manual code. However, technically it is possible and the default way to customize EMF classes51 . Xtext provides a specialized Ecore generator
52

, which allows to add a custom class where the

manual maintained code is added and thus follows the Generation Gap Pattern. This works with a coding convention: The custom class must be in the same package as the generated class (org.xtext.lwc.instances.instances.impl) and has the same name as the extended class, adding the sux Custom. Create the package org.xtext.lwc.instances.instances.impl in directory src and add the class ObjectLiteralImplCustom
1 2 3 4 5 6 7 8 53

package org.xtext.lwc.instances.instances.impl; public class ObjectLiteralImplCustom extends ObjectLiteralImpl { @Override public String getSimpleName() { return "this"; } }

50

JvmIdentifiableElement represents any object that can be referred to in the Java language, e.g. classes,

variables, methods, parameters etc. EMF uses JET as code generation engine and JMerge for merging manual and generated code 52 see presentation Code Generation Goodies from Moritz Eysholdt (EclipseCon 2011) 53 https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/
51

org/xtext/lwc/instances/instances/impl/ObjectLiteralImplCustom.java

74

LWC11 -

Submission

Run the workow again. You can now verify that EMF will use the custom implementation instead of its own generated one by opening InstancesFactoryImpl in the emf-gen folder. The createObjectLiteral() method is now dened as follows:
1 2 3 4 5

public ObjectLiteral createObjectLiteral() { ObjectLiteralImplCustom objectLiteral = new ObjectLiteralImplCustom(); return objectLiteral; }

75

LWC11 -

Submission

7.6 Implementing the Scope Provider


We need to implement scoping for this DSL. An empty implementation has been generated to class org.xtext.lwc.instances.scoping.InstancesScopeProvider
1 2 3 4 5 6 7 54 55

package org.xtext.lwc.instances.scoping; @SuppressWarnings("all") public class InstancesScopeProvider extends XbaseScopeProvider { @Override public IScope createLocalVarScopeForBlock(final XBlockExpression block, final int indexOfContextExpressionInBlock, final IScope parentScope) { IEObjectDescription objDesc = EObjectDescription.create(QualifiedName.create("this"), block); return MapBasedScope.createScope(parentScope, Lists.newArrayList(objDesc)); } public IScope createLocalVarScope(final EObject context, final EReference reference, final IScope parentScope, final boolean includeCurrentBlock, final int idx) { if (context instanceof Instance) { Iterable<XVariableDeclaration> localVars = Iterables.filter(((Instance) context). getExpressions(), XVariableDeclaration.class); return MapBasedScope.createScope(parentScope, Scopes.scopedElementsFor(localVars)); } else { return super.createLocalVarScope(context, reference, parentScope, includeCurrentBlock, idx); } } }

8 9

10 11 12 13

14 15 16

17 18 19

20 21 22

54

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/scoping/InstancesScopeProvider.java Import statements have been removed in the code here.

55

76

LWC11 -

Submission

8 Phase 1.2 - Implement Runtime Type Systems


Now it is time to add type information to the concepts implemented in both DSLs. This is mandatory if DSLs make use of the JVM Types Model or Xbase. The current state of both DSLs are not fully functional yet, but after nishing this step the DSLs will nally integrate with each other.

8.1 Domainmodel DSL


8.1.1 JVM Model Inference We changed our grammar to refer to JVM types for the Entity superType and Property type references. Now we can use any type referrable from the JVM for type references. However, until now it is not known how our concepts Entity and Property map to JVM type concepts. Therefore we have to infer a JVM model from the objects of our metamodel. Roughly this means we have to tell Xtext that an Entity is a Class, which has instance variables and getter/setter methods for what we call Property. This we refer to as JVM Model Inference, and its actually a way to implement a runtime type system. There are other ways to do so also, and an alternative to this is using the xtexttypesystem framework56 . We do not follow this alternative more now. By deriving our language from Xbase a stub for a Xtend class was generated, DomainmodelJvmModelInferrer.xtend in subpackage jvmmodel. This class must be implemented to realize the typesystem for our language. It has to implement the IJvmModelInferrer interface
1 2 3 4 5 6 7 8 57

public interface IJvmModelInferrer { /** * Returns the list of inferred declared types for a given source object. * @return the list of inferred types. May not be <code>null</code>. */ List<? extends JvmDeclaredType> inferJvmModel(EObject sourceObject); ... }

56 57

http://code.google.com/a/eclipselabs.org/p/xtext-typesystem/ http://download.eclipse.org/modeling/tmf/xtext/javadoc/2.0.0/org/eclipse/xtext/xbase/ jvmmodel/IJvmModelInferrer.html

77

LWC11 -

Submission

The sourceObject will be the root object of a DSL model, in our case it is of type DomainModel. Replace now the content by the following
1 2 3 4 5 6 58

, we will go into some details right after again:

package org.eclipse.xtext.example.domainmodel.jvmmodel import org.eclipse.xtext.example.domainmodel.domainmodel.* import org.eclipse.xtext.example.domainmodel.* import org.eclipse.xtext.xbase.jvmmodel.*

import org.eclipse.emf.ecore.* 7 import org.eclipse.xtext.common.types.*


8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
58

import org.eclipse.xtext.common.types.util.* import static org.eclipse.xtext.common.types.* import java.util.* import com.google.inject.Inject import static org.eclipse.xtext.EcoreUtil2.* class DomainmodelJvmModelInferrer implements IJvmModelInferrer { @Inject TypesFactory typesFactory @Inject extension IJvmModelAssociator jvmModelAssociator @Inject extension JvmVisibilityExtension jvmVisibilityExtension @Inject extension DomainmodelExtensions domainmodelExtensions override List<JvmDeclaredType> inferJvmModel(EObject sourceObject) { sourceObject.disassociate transform( sourceObject ).toList } def dispatch Iterable<JvmDeclaredType> transform(DomainModel model) { model.elements.map(e | transform(e)).flatten } def dispatch Iterable<JvmDeclaredType> transform(Entity entity) { val jvmClass = typesFactory.createJvmGenericType jvmClass.simpleName = entity.name jvmClass.packageName = entity.packageName entity.associatePrimary(jvmClass) jvmClass.makePublic if (entity.superType != null) https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase12/DomainmodelJvmModelInferrer.xtend

78

LWC11 jvmClass.superTypes += cloneWithProxies(entity.superType) for(f : entity.features) { transform(f, jvmClass) } newArrayList(jvmClass as JvmDeclaredType) } def dispatch Iterable<JvmDeclaredType> transform(Import importDecl) { emptyList } def dispatch void transform(Feature f, JvmGenericType type) {} def dispatch void transform(Property property, JvmGenericType type) { val jvmField = typesFactory.createJvmField jvmField.simpleName = property.name jvmField.type = cloneWithProxies(property.type) jvmField.makePrivate type.members += jvmField property.associatePrimary(jvmField) val jvmGetter = typesFactory.createJvmOperation jvmGetter.simpleName = "get" + property.name.toFirstUpper jvmGetter.returnType = cloneWithProxies(property.type) jvmGetter.makePublic type.members += jvmGetter property.associatePrimary(jvmGetter) val jvmSetter = typesFactory.createJvmOperation jvmSetter.simpleName = "set" + property.name.toFirstUpper val parameter = typesFactory.createJvmFormalParameter parameter.name = property.name.toFirstUpper parameter.parameterType = cloneWithProxies(property.type) jvmSetter.makePublic jvmSetter.parameters += parameter type.members += jvmSetter property.associatePrimary(jvmSetter) } }

Submission

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

After saving the Xtend class is compiled to the xtend-gen folder again automatically. Note that we do not generate Java classes with the JVM Model Inferer here, and although our code generator does produce Java classes for entities, these compiled Java classes are actually

79

LWC11 -

Submission

not required to resolve JVM type references. The JVM Model Inferers job is to compute a model of types that represent the concepts of the language as if they were written as classes in Java. You can say, for better understanding, the Inferer simulates compiled Java code. We explained Injection and Extensions in Xtend already when we implemented the code generator in Phase 0.2. So we do not explain the concept here again. But what we inject here should be mentioned now:
1 2 3 4 5 6 7

@Inject TypesFactory typesFactory @Inject extension IJvmModelAssociator jvmModelAssociator @Inject extension JvmVisibilityExtension jvmVisibilityExtension @Inject extension DomainmodelExtensions domainmodelExtensions

TypeFactory This is the EMF Factory class of the org.eclipse.xtext.common.types.TypesPackage EPackage. This package denes an EMF model for the concepts of the Java language, like Classes, Operations etc. It is not the model used by Eclipses JDT, it is part of the Xtext framework, as you see also from the package. We use this factory to instantiate types from the JVM model.

IJvmModelAssociator The source objects from our DSL model are connected to Java classes that are generated from the type system description that is dened in the JVM Model Inferer. The methods from this interface are used to establish and release connections between the source objects and target classes. The interface denes 3 methods:
public interface IJvmModelAssociator { 2 void associate(EObject sourceElement, EObject jvmElement);
1 3 4 5

void associatePrimary(EObject sourceElement, EObject jvmElement); void disassociate(EObject rootElement); }

JvmVisibilityExtension This is just a utility extension to set the visibility of JVM Model elements. Internally the visibility is a enum literal, but there is a known limitation of Xtend59 to handle enum literals properly at the moment.
59

https://bugs.eclipse.org/bugs/show_bug.cgi?id=342848, scheduled for Indigo SR1

80

LWC11 DomainmodelExtensions

Submission

This extension was added in Phase 0.2 of this tutorial. We need it here to use the packageName() function.

8.1.2 Detailed Description Of The Transformation This section will now discuss the JVM Model Inference in detail. In principle this is a modelto-model transformation, but we will see another one in the solution of task 1.3.
1 2 3 4

override List<JvmDeclaredType> inferJvmModel(EObject sourceObject) { sourceObject.disassociate transform(sourceObject).toList }

inferJvmModel() is the main method we have to implement. The result is a list of JVM types, which is created by the transform() method. Note that there are also other methods here called transform() with dierent argument types. We make again use of Polymorphic Dispatch, signalled by the dispatch keyword. sourceObject.disassociate removes the association between the source object and transformed JVM model, which might was created in a previous transformation.
1 2 3

def dispatch Iterable<JvmDeclaredType> transform(DomainModel model) { model.elements.map(e | transform(e)).flatten }

This transform() method iterates over all model elements from the elements reference and will call the appropriate transform() method for each of them. The map() function is comparable to OCLs or Xtend1s collect. The result is again an Iterable, but with the mapped target types. Note that there is no return statement here. The value of the last expression denotes the result type and value.
1 2 3 4 5 6 7 8

def dispatch Iterable<JvmDeclaredType> transform(Entity entity) { val jvmClass = typesFactory.createJvmGenericType jvmClass.simpleName = entity.name jvmClass.packageName = entity.packageName entity.associatePrimary(jvmClass) jvmClass.makePublic if (entity.superType != null) jvmClass.superTypes += cloneWithProxies(entity.superType)

81

LWC11 for(f : entity.features) { transform(f, jvmClass) } newArrayList(jvmClass as JvmDeclaredType) }

Submission

9 10 11 12 13

In this transform() method an Entity object is mapped to an instance of JvmGenericType (subclass of JvmDeclaredType). The instance is created with the TypesFactory. Then name and packageName are set, where entity.packageName calls the extension method from DomainmodelExtensions.xtend. At the end transform() for all features of the Entity is called, which will call the method with the Property argument type.
1 2 3

def dispatch Iterable<JvmDeclaredType> transform(AbstractElement e) { emptyList }

transform() will be called for all elements of a DomainModels elements reference, thus it can be called for any subtype of AbstractElement. If Polymorphic Dispatch is used and a call of a function for a non-matched type is called, Xtend will internally throw an IllegalArgumentException. If you look into the derived Java class DomainmodelJvmModelInferer.java this will look this:
60 1 2 3 4 5 6 7 8 9 10 11

public Iterable<JvmDeclaredType> transform(final EObject entity) { if ((entity instanceof Entity)) { return _transform((Entity)entity); } else if ((entity instanceof AbstractElement)) { return _transform((AbstractElement)entity); } else if ((entity instanceof DomainModel)) { return _transform((DomainModel)entity); } else { throw new IllegalArgumentException(); } }

Therefore it is required to handle also instances of AbstractElement which are no Entity, and just return an empty list of declared types then.
1 2
60

def dispatch void transform(Property property, JvmGenericType type) { val jvmField = typesFactory.createJvmField Note the order of queried types: It is important to prove for type Entity before its supertype AbstractElement. Xtend will care about this, the order of declaring the functions dont matter.

82

LWC11 jvmField.simpleName = property.name ... }

Submission

3 4 5

This method creates an instance of JvmField for a Property instance and sets its name. The value for type is a JvmTypeReference and is a containment reference for both JvmField and Property. If we would just assign the type from the Property to the JvmField the reference type for the property would be null afterwards, since the instance would now be owned by the JvmField instance61 . Thus, the JvmTypeReference is copied.
1

jvmField.type = cloneWithProxies(property.type)

The eld is set to visibility private and the eld is added to the members reference of JvmField.
1 2

jvmField.makePrivate type.members += jvmField

The property is now associated with the eld.


1

property.associatePrimary(jvmField)

Finally, a getter and setter method is created, which will be of type JvmOperation. The getter method is necessary, since the created eld was set to private visibility and without the setter method the eld would not be assignable. Both will be required by the Instance DSL, which accesses the inferred elds. We skip repeating the setter method code here. Note that the property is also associated with the operations.
1 2 3 4 5 6

val jvmGetter = typesFactory.createJvmOperation jvmGetter.simpleName = "get" + property.name.toFirstUpper jvmGetter.returnType = cloneWithProxies(property.type) jvmGetter.makePublic type.members += jvmGetter property.associatePrimary(jvmGetter)

8.1.3 Testing the Domainmodel DSL Finally our Domainmodel DSL is functional again. Start the runtime workbench now.

61

This is how EMF handles containment references. Setting a contained element to another container automatically removes it from its old container.

83

LWC11 -

Submission

You can remove the Datatypes.dmodel le. Datatypes must not be declared anymore, and the datatype keyword was removed anyway. On the other side, like in Java, types must be either imported or qualied. Remember that the packageName() function in DomainmodelExtensions.xtend returns hard-coded entities for an Entity instance for now. Replace the content of Person.dmodel by:
1 2 3 4 5 6 7 8 9 10

import java.util.Date import entities.* entity Person { name : String firstname : String birthdate : Date ownedCar : Car salary : java.math.BigDecimal }

Try a bit around. All cross-references are valid now again.

More than this: you can refer now to any Java class as a Propertys type in the Domainmodel Language. Add a Java class to your project and a Property to the Person Entity of that type.

84

LWC11 -

Submission

8.2 Instance DSL


8.2.1 Type Provider Now lets implement the type system for the Instance DSL. For now the Instance DSL is not aware of what type the initialization block (ObjectLiteral) eectively is. Remember again the example that should be implemented:
1 2 3 4

Car c = { make = "VW" model = "Touran" }

In this example the content of the block expression must be of Car, then it would be clear that make must be a eld of Car. Also it is not known yet that a DateLiteral is eectively of type java.util.Date, its just a literal for now. This type information must be implemented by a so-called Type Provider. Add the package org.xtext.lwc.instances.typesystem
62

and

create

the

le

InstancesTypeProvider.xtend
1 2 3 4 5

package org.xtext.lwc.instances.typesystem import org.eclipse.xtext.xbase.typing.XbaseTypeProvider import org.eclipse.xtext.xbase.XBlockExpression import org.xtext.lwc.instances.instances.DateLiteral


62

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/typesystem/InstancesTypeProvider.xtend

85

LWC11 import org.xtext.lwc.instances.instances.ObjectLiteral import java.util.Date import com.google.inject.Singleton @Singleton class InstancesTypeProvider extends XbaseTypeProvider { def dispatch type(ObjectLiteral block, boolean rawTypeOnly) { val expected = getExpectedType(block, rawTypeOnly) if (expected==null) return typeReferences.getTypeForName(typeof(Object),block) return expected } def dispatch type(DateLiteral date, boolean rawTypeOnly) { return typeReferences.getTypeForName(typeof(Date), date) } def dispatch typeForIdentifiable(ObjectLiteral literal, boolean rawType) { return getExpectedType(literal, rawType) } }

Submission

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

The
1 2 3 4 5 6 7 8 9 10 11

custom

Type

Provider

must

be

registered

in

the

Guice

runtime

module

InstancesRuntimeModule:
package org.xtext.lwc.instances; import org.eclipse.xtext.xbase.typing.ITypeProvider; import org.xtext.lwc.instances.typesystem.InstancesTypeProvider; /** * Use this class to register components to be used at runtime / without the * Equinox extension registry. */ @SuppressWarnings("restriction") public class InstancesRuntimeModule extends org.xtext.lwc.instances. AbstractInstancesRuntimeModule {
12 13 14 15 16

@Override public Class<? extends ITypeProvider> bindITypeProvider() { return InstancesTypeProvider.class; } }

86

LWC11 8.2.2 Testing The Instance DSL

Submission

Start your runtime workbench again, it is time to use the Instance DSL. Add a le Sample.instance and copy this content into it:
1 2 3 4 5 6 7 8 9 10 11 12 13

import entities.* Person p = { name = "Voelter" firstname = "Markus" birthdate = 14.02.1977 ownedCar = c } Car c = { make = "VW" model = "Touran" }

The DSL is now aware of the dened entities from our Domainmodel Language les. You can navigate to them as you are already used to within one DSL.

87

LWC11 -

Submission

9 Phase 1.3 - How To Do A Model-To-Model Transformation


In principle we have already seen a model-to-model transformation with Xtend when the DomainmodelJvmModelInferrer was implemented. But lets implement the creation of a simple database model from the .dmodel models from scratch.

88

LWC11 -

Submission

9.1 Creating The Project


Of course we could implement the transformation also in the existing runtime project, but things should be better kept seperated. Create an Empty EMF Project with the project wizard and name the project org.eclipse.xtext.example.domainmodel.m2m.

89

LWC11 -

Submission

The project will require some additional dependencies. Open the MANIFEST.MF le, go to the Dependencies page and add org.eclipse.xtext.example.domainmodel, org.eclipse.xtext.xtend2.lib and org.eclipse.xtext.xbase as Required Plug-ins.

9.2 Creating The Ecore Target Metamodel


Before the transformation can be implemented we need at rst a metamodel to which we transform. We will create a small Ecore metamodel that denes the concepts Database, Table, Column and a common supertype NamedElement. The structure should be this:
NamedElement -name : EString

Database

-tables 0..* Table -columns 0..* Column

Select the model folder and choose the menu entry File / New / Other / Eclipse Modeling Framework / Ecore Model and press Finish. The Ecore editor opens now. Select the package, open the Properties View and enter: Name: dbmodel Ns Prex: dbmodel Ns URI: http://www.xtext.org/example/Databasemodel In this EPackage create the EClasses NamedElement Abstract= true
63

63

. Go to the next wizard page and enter the le name Database.ecore

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel.m2m/model/Database.ecore

90

LWC11 EAttribute: Name=name, EType=EString Column ESuperTypes: NamedElement EAttribute: Name=type, EType=EString Table ESuperTypes: NamedElement EReference: Name=columns, Containment=true, Upper Bound=-1, EType=Column Database ESuperTypes: NamedElement EReference: Name=tables, Containment=true, Upper Bound=-1, EType=Table

Submission

Select the root node in the editor and choose Validate from the context menu. You should get no errors reported.

In the Package Explorer View select the Database.ecore le and choose File / New / Other / Eclipse Modeling Framework / EMF Generator Model. On the next wizard page the le name Database.genmodel should be already entered. Press two times Next, then the Load button, Next and Finish. Now the Genmodel editor opens. Open the root node, select the Dbmodel package and open the Properties view. In the All section enter a value for Base Package: org.eclipse.xtext.example.domainmodel.

91

LWC11 -

Submission

Open the context menu on the root node and select Generate Model Code from the context menu.

Your project should now look like this:

92

LWC11 -

Submission

9.3 Implementing The Xtend Transformation Class


Add a source folder xtend-gen to the project. Create a package org.eclipse.xtext.example.domainmodel.m2m. Then select the package and create Domainmodel2Dbmodel.xtend. You will be asked to add the Xtext nature, just conrm the popup dialog. Add the folowing implementation
1 2 3 4 5 6 7 8 9 64

package org.eclipse.xtext.example.domainmodel.m2m import org.eclipse.xtext.example.domainmodel.domainmodel.* import org.eclipse.xtext.example.domainmodel.dbmodel.* import java.util.* import com.google.inject.Inject class Domainmodel2Dbmodel { def create target : DbmodelFactory::eINSTANCE.createDatabase transform (List<Entity> entities) { for (entity : entities) { target.tables += transformEntity2Table(entity) } } def create target : DbmodelFactory::eINSTANCE.createTable transformEntity2Table (Entity source) { target.name = source.name val idColumn = DbmodelFactory::eINSTANCE.createColumn idColumn.name = "ID" idColumn.type = "INTEGER" target.columns += idColumn target.columns += source.features.filter(typeof(Property)).map(x|x. transformProperty2Column); } def create target : DbmodelFactory::eINSTANCE.createColumn transformProperty2Column ( Property source) { target.name = source.name target.type = getColumnType(source) } def getColumnType (Property source) { switch (source.type.simpleName) {
64

10 11 12 13 14 15

16 17 18 19 20 21

22 23 24

25 26 27 28 29 30

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel.m2m/src/org/eclipse/xtext/example/domainmodel/m2m/Domainmodel2Dbmodel.xtend

93

LWC11 case "Boolean" : "BIT" case "String" : "VARCHAR" case "BigDecimal" : "DECIMAL" case "Date" : "DATE" default: "INTEGER" // type references } } }

Submission

31 32 33 34 35 36 37 38

A typical problem when doing model-to-model or graph transformations is to ensure that objects are created just once and that you can refer later to the same instance again. This is typically done by splitting the transformation process into two phases: the tree construction and the linking phase. With Xtend this is not necessary. Transformations can be done in one phase by using the create modier for a function. If adding create you will always get the same instance for every call with the same set of arguments. The object will be created on the rst call, and with every subsequent call you will get back the cached target object. However, the transformation here does not need this feature, since we do not have any cross references to ll in the target metamodel. The task is just too trivial to really show the value of create functions.
1 2

def create target : DbmodelFactory::eINSTANCE.createTable transformEntity2Table (Entity source) { ... }

A create function requires that the target object will get a name to which you can refer to it in the denition block (here: target). This is followed by a colon : and an expression that instantiates the desired target object. In our case we address the EMF factory instance (DbmodelFactory::eINSTANCE) and call its create methods.
1 65

def create target : DbmodelFactory::eINSTANCE.createDatabase transform (List<Entity> entities) { for (entity : entities) { target.tables += transformEntity2Table(entity) } }

2 3 4 5

This will be the main entry point of our transformation. It is expected that a list of Entity instances will be passed when calling the transform() function, and the result will be an instance of our target metamodel of type Database.
65

It would be nicer to bind the factory instance in a Guice conguration and inject the instance with @Inject into the transformation, but it requires a bit more coding and we want to keep things as short as possible here

94

LWC11 -

Submission

For each Entity the function transformEntity2Table() is called, which creates an instance of Table. Note the += operator: The reference tables is multi-valued and we can add an object just by using the += operator.
66

66

In fact Xbase allows even operator overloading

95

LWC11 -

Submission

9.4 Creating The Transformation Workow


The transformation itself is not of use without being able to execute it. You could use it now just by instantiating the Domainmodel2Dbmodel Java class67 and call its transform() method with a list of entities. But we want to integrate the transformation somehow in our workbench. This will be done now with MWE2, but there are also other ways how one could enable this. Even execution on every save of models is possible by using Xtexts builder infrastructure.

9.4.1 Custom MWE2 Workow Component For execution of the transformation we will write a custom MWE2 worow component. In package org.eclipse.xtext.example.domainmodel.m2m
68 69

create

class

Domainmodel2DbmodelWfComponent.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

package org.eclipse.xtext.example.domainmodel.m2m; @SuppressWarnings("restriction") public class Domainmodel2DbmodelWfComponent extends AbstractWorkflowComponent2 { private String modelSlot; private String outputSlot; public void setModelSlot(String modelSlot) { this.modelSlot = modelSlot; } public void setOutputSlot(String outputSlot) { this.outputSlot = outputSlot; } @Override protected void checkConfigurationInternal(Issues issues) { checkRequiredConfigProperty("modelSlot", modelSlot, issues); checkRequiredConfigProperty("outputSlot", outputSlot, issues); } @Override protected void invokeInternal(WorkflowContext ctx, ProgressMonitor m, Issues issues) {
67 68

in the xtend-gen folder Import statements were removed here 69 https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse. xtext.example.domainmodel.m2m/src/org/eclipse/xtext/example/domainmodel/m2m/ Domainmodel2DbmodelWfComponent.java

96

LWC11 Collection<?> slotContent = (Collection<?>) ctx.get(modelSlot); if (slotContent==null) { issues.addError(String.format("Slot %s is empty", modelSlot)); return; } List<Entity> entities = Lists.newArrayList(); for (Resource r : Iterables.filter(slotContent, XbaseResource.class)) {

Submission

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

for (Entity e : EcoreUtil2.eAllOfType(r.getContents().get(0), Entity.class)) { entities.add(e); } } if (entities.isEmpty()) { issues.addError("No Entity instance found in model slot"); return; } // execute the transformation Database transformed = new Domainmodel2Dbmodel().transform(entities); ctx.set(outputSlot, transformed); } }

This component will expect a collection of elements in the slot70 which is specied by the modelSlot property. This collection of elements will contain EMF resources of type XbaseResource. Each of them represents one .dmodel le, and they will contain instances of Entity, which are collected in the entities list. At the end the Domainmodel2Dbmodel class is instantiated and the transformation is invoked. The resulting Database instance will be stored in the slot specied by the outputSlot property.

9.4.2 Implementing The Workow Create the le Domainmodel2Dbmodel.mwe2


1 2 3 4 71

in the same package:

module Domainmodel2Dbmodel var modelPath var targetUri


70

Slot is a MWE2 concept, just think of a HashMap here, its actually nothing else. Slots are the way how workow components can communicate state during workow execution. https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel.m2m/src/org/eclipse/xtext/example/domainmodel/m2m/Domainmodel2Dbmodel.mwe2

71

97

LWC11 -

Submission

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

Workflow { component = org.eclipse.xtext.mwe.Reader { path = modelPath // this class will be generated by the xtext generator register = org.eclipse.xtext.example.domainmodel.DomainmodelStandaloneSetup {} loadResource = { slot = "model" } } // invoke the transformation component = org.eclipse.xtext.example.domainmodel.m2m.Domainmodel2DbmodelWfComponent { modelSlot = "model" outputSlot = "transformed" } // store XMI file component = org.eclipse.emf.mwe.utils.Writer { modelSlot = "transformed" uri = targetUri } }

The workow is simple: 1. Declare two variables modelPath and targetUri. They have no values here, so the values must be specied when invoking the workow. 2. Read recursively all .dmodel les from modelPath and store the result in the slot named model. 3. Call the custom transformation component, read the objects from slot model and store the result in slot transformed. 4. Serialize the resulting Database object with EMF to XMI format to the le specied by targetUri.

9.4.3 Package Export The new package must now be exported in the plugin manifest, otherwise you cannot use the classes from this package. Open MANIFEST.MF, go to the Runtime page and add org.eclipse.xtext.example.domainmodel.m2m to the Exported Packages list. You will have

98

LWC11 -

Submission

to check the Show non-Java packages when you pressed the Add button to see the package listed.

99

LWC11 -

Submission

9.5 Testing The Transformation


9.5.1 Add Runtime Dependencies For The Test Project Start your runtime workbench again. The lwc11-test project will need some modications to execute the workow. First, the project must be converted to a plugin project. Right click on the project and select Congure / Convert to Plug-in Projects. This will create the MANIFEST-MF le and add the Plugin-Dependencies as classpath container for the project. Open the MANIFEST-MF le and add the following required plugins on the Dependencies page: 1. org.eclipse.xtext.example.domainmodel 2. org.eclipse.xtext.example.domainmodel.m2m 3. org.eclipse.emf.mwe2.launch 4. org.apache.commons.logging 5. org.apache.log4j 6. org.eclipse.xtext.xtend2.lib 7. org.eclipse.xtext.xbase

9.5.2 Create Launch Conguration Now open the Run / Run Congurations dialog and add a Mwe2 Launch conguration. On the Main page specify Project: lwc11-test Main-class: org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher You can use the Search button if you want.

100

LWC11 -

Submission

Go to the Refresh page and check the option Refresh resources upon completion. This will update the workspace after execution of the transformation. Next go the Arguments page and enter into Program arguments:
1 2 3

Domainmodel2Dbmodel -pmodelPath=${project_loc:lwc11-test}/src -ptargetUri=file:${project_loc:lwc11-test}/src-gen/lwc11-test-db.xmi

101

LWC11 -

Submission

If you want to store the launch conguration as a le in the project, go to the Common page and choose Save as: Shared le.

9.5.3 Executing The Transformation Now press the Run button. The transformation process is executed and will create the le lwc11-test-db.xmi into the src-gen folder. You can open the le with the Ecore editor now:

102

LWC11 -

Submission

Or if you open the XMI le with the text editor:


1 2

<?xml version="1.0" encoding="ASCII"?> <dbmodel:Database xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www .w3.org/2001/XMLSchema-instance" xmlns:dbmodel="http://www.xtext.org/example/ Databasemodel" xsi:schemaLocation="http://www.xtext.org/example/Databasemodel java://org .eclipse.xtext.example.domainmodel.dbmodel.DbmodelPackage">

3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

<tables name="Car"> <columns name="ID" type="INTEGER"/> <columns name="make" type="VARCHAR"/> <columns name="model" type="VARCHAR"/> </tables> <tables name="Person"> <columns name="ID" type="INTEGER"/> <columns name="name" type="VARCHAR"/> <columns name="firstname" type="VARCHAR"/> <columns name="birthdate" type="DATE"/> <columns name="ownedCar" type="INTEGER"/> <columns name="salary" type="DECIMAL"/> <columns name="employee_of" type="INTEGER"/> </tables> </dbmodel:Database>

103

LWC11 -

Submission

10 Phase 1.4 - Introducing A Namespace Concept


In this section the Domainmodel Language is extended by a namespace concept. To use Entities from dierent namespaces we need also a concept to qualify the names of Entities or to import namespaces.

10.1 Grammar Changes


Open Domainmodel.xtext, replace the contents as follows again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
72

72

and run the generator workow

grammar org.eclipse.xtext.example.domainmodel.Domainmodel with org.eclipse.xtext.xbase.Xbase generate domainmodel "http://www.xtext.org/example/Domainmodel" DomainModel: elements+=AbstractElement*; AbstractElement: PackageDeclaration | Entity | Import; Import: import importedNamespace=QualifiedNameWithWildCard; PackageDeclaration: "package" name=QualifiedName "{" elements+=AbstractElement* "}"; Entity: "entity" name=ID ("extends" superType=JvmTypeReference)? "{" features+=Feature* "}"; Feature: Property; Property: name=ID ":" type=JvmTypeReference;

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase14/Domainmodel.xtext

104

LWC11 QualifiedNameWithWildCard : QualifiedName (. *)?;

Submission

30 31

Lets go through the individual changes in detail:


1 2 3 4

PackageDeclaration: "package" name=QualifiedName "{" elements+=AbstractElement* "}";

This introduces the namespace concept PackageDeclaration. It is expected that elements contained in a package or within a {} block. A package can contain any AbstractElement, which can be a package again. Therefore the AbstractElement rule has been extended.
1 2

AbstractElement: PackageDeclaration | Entity | Import;

Since the grammar has changed, rerun the Xtext code generator.

10.2 Generator Changes


The latest grammar changes require changes in the generator code. The package for Entity objects was hard-coded entities so far, but with introduction of Package Declarations we want to use this also in our generator. Here we will see again why we use Extensions. The generator class (DomainmodelGenerator) itself does not require changes, only the extension les that are used by the generator. Open DomainmodelExtensions.xtend and replace the content by
1 2 3 4 5 6 7 8 9 10 11
73

73

package org.eclipse.xtext.example.domainmodel import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.common.types.JvmDeclaredType import org.eclipse.xtext.example.domainmodel.domainmodel.* class DomainmodelExtensions { /** * computes the qualified name if its * a PackageDeclaration, an Entity or a JvmDeclaredType * returns null otherwise https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/DomainmodelExtensions.xtend

105

LWC11 */ def String packageName(Object o) { switch(o) { PackageDeclaration : concatPath(packageName(o.eContainer), o.name) EObject : packageName(o.eContainer) JvmDeclaredType : o.packageName default: null } } def concatPath(String prefix, String suffix) { if (prefix.nullOrEmpty) suffix else prefix + "." + suffix } }

Submission

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

The packageName() has been extended. For any EObject, like Entity, the containment hierarchy is looked up by calling packageName() on the eContainer. If the container is of type PackageDeclaration, the name will be concatenated from each package in the hierarchy. This is again a good example of using the extended switch statement in Xtend together with Polymorphic Dispatch with (note the type checking here) and the use of recursive functions.

10.3 JVM Model Inference


The JVM Model Inference must be extended. Open DomainmodelJvmModelInferrer.xtend and add
1 2 3 4 74

def dispatch Iterable<JvmDeclaredType> transform(PackageDeclaration packageDecl) { packageDecl.elements.map(e | transform(e)).flatten }

This will return the list of declared elements within the package by calling transform() again. Since the contained elements might be again of type PackageDeclaration, flatten is called to avoid to have nested iterables in the result.

74

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase14/DomainmodelJvmModelInferrer.xtend

106

LWC11 -

Submission

10.4 Testing Package Declarations


Start the runtime workbench again. Delete the content of the src-gen folder (not the folder itself!) and clean the project (Project / Clean) to trigger a build. Car.java and Person.java will go to the default package in src-gen now75 . Lets change the model les:

Person.dmodel

76

1 2 3 4 5 6 7 8 9 10 11 12 13

import java.util.Date import p2.Car package p1 { entity Person { name : String firstname : String birthdate : Date ownedCar : Car salary : java.math.BigDecimal employee_of : example.Company } }

Car.dmodel
package p2 { /**

77

1 2 3 4 5 6 7 8 9

* Think about some meaningful documentation here... */ entity Car { make : String model : String } }
75

It might be necessary to clean the project twice, since Person.dmodel can only be compiled when Car.java exists. https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/ phase14/Person.dmodel https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/lwc11-test/src/Car.dmodel

76

77

107

LWC11 Sample.instance
78

Submission

1 2 3 4 5 6 7 8 9 10 11

p1.Person p = { name = "Voelter" firstname = "Markus" birthdate = 14.02.1977 ownedCar = c } p2.Car c = { make = "VW" model = "Touran" }

After applying these changes all cross references should be resolved normally and the generator will produce the Java les for the entities into the packages p1 and p2.

78

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/lwc11-test/src/Sample. instance

108

LWC11 -

Submission

11 Phase 1.5 - Integrating Manually Written Code


In this section the Domainmodel DSL will be extended to add operations to Entities, in which complex expressions and even Java calls can be dened. Again the key is Xbase, which already provides the necessary base rules that we need. Without Xbase it would be much harder, though not impossible, to integrate Java into an external DSL. The result will allow to dene operations like this:
1 2 3 4 5 6 7

entity Person { name : String firstname : String op getFullName () : String { firstName + " " + lastName } }

Enabling full support for Xbase and thus Java integration will require some amount of coding, but comparing the coding required to what you gain then it is just really little.

11.1 Grammar Changes


Open Domainmodel.xtext and copy this content
1 2 3 4 79

grammar org.eclipse.xtext.example.domainmodel.Domainmodel with org.eclipse.xtext.xbase.Xbase generate domainmodel "http://www.xtext.org/example/Domainmodel"

DomainModel: 6 elements+=AbstractElement*;
5 7 8 9 10 11 12 13 14 15 16

AbstractElement: PackageDeclaration | Entity | Import; Import: import importedNamespace=QualifiedNameWithWildCard; PackageDeclaration: "package" name=QualifiedName "{" elements+=AbstractElement*
79

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/Domainmodel.xtext

109

LWC11 "}"; Entity: "entity" name=ID ("extends" superType=JvmTypeReference)? "{" features+=Feature* "}"; Feature: Property | Operation; Property: name=ID ":" type=JvmTypeReference; Operation:

Submission

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

visibility=Visibility? op name=ID ( (params+=JvmFormalParameter (, params+= JvmFormalParameter)*)? ) : type=JvmTypeReference body=XBlockExpression; enum Visibility: public | private | protected; QualifiedNameWithWildCard : QualifiedName (. *)?;

32 33 34 35 36 37 38

Most notably is the Operation rule. It is a subtype of Feature now. Important is the body attribute which is of type XBlockExpression. XBlockExpression allows the usage of Xbase expressions within a block surrounded by curly brackets80 . Now run the Xtext generator again to create the new DSL implementation. We will get a compile error in DomainmodelJavaValidator now, since the feature name from property will be now pulled up into the Feature base class when Xtext infers the Ecore metamodel for the grammar. Open the class and change the constant PROPERTY__NAME to FEATURE__NAME in method checkFeatureNameStartsWithLowercase() to resolve the error.

11.2 Runtime Changes


To support the operations properly in our DSL some customizations to our runtime project are required. We dont get in too deep now to explain all of them. Just follow the instructions.
80

We used this already in the Instance DSL

110

LWC11 11.2.1 JVM Model Inference The JVM Model Inference must
81

Submission

be

extended

again.

Open

DomainmodelJvmModelInferrer.xtend and add


1 2 3 4 5 6 7 8 9 10

def dispatch void transform(Operation operation, JvmGenericType type) { val jvmOperation = typesFactory.createJvmOperation jvmOperation.simpleName = operation.name jvmOperation.returnType = cloneWithProxies(operation.type) jvmOperation.parameters.addAll(operation.params.map(p|cloneWithProxies(p))) jvmOperation.makePublic type.members += jvmOperation operation.associatePrimary(jvmOperation) }

11.3 Type Provider


Similar to the Instance DSL the Domainmodel DSL now needs a Type Provider implementation. This is needed to compute the expected return type of an Operation from the content of the Operations body. Add a package ...domainmodel.typing and the class DomainmodelTypeProvider.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 82

package org.eclipse.xtext.example.domainmodel.typing; import org.eclipse.emf.ecore.EReference; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.example.domainmodel.domainmodel.DomainmodelPackage; import org.eclipse.xtext.example.domainmodel.domainmodel.Operation; import org.eclipse.xtext.xbase.typing.XbaseTypeProvider; import com.google.inject.Singleton; @SuppressWarnings("restriction") @Singleton public class DomainmodelTypeProvider extends XbaseTypeProvider { /**
81

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse. xtext.example.domainmodel/src/org/eclipse/xtext/example/domainmodel/jvmmodel/ DomainmodelJvmModelInferrer.xtend https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/typing/DomainmodelTypeProvider.java

82

111

LWC11 * The operation body is expected to return the declared return type of * the operation itself.

Submission

15 16 17

* @return the operations return type or null if queried with another reference or for an invalid reference. */ protected JvmTypeReference _expectedType(Operation operation, EReference reference, int index, boolean rawType) { if(reference == DomainmodelPackage.Literals.OPERATION__BODY) { return operation.getType(); } return null; } }

18 19

20 21 22 23 24 25

11.3.1 Simple Name Provider Add the Xtend class DomainmodelIdentifiableSimpleNameProvider.xtend in subpackage jvmmodel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 83

package org.eclipse.xtext.example.domainmodel.jvmmodel import org.eclipse.xtext.xbase.featurecalls.IdentifiableSimpleNameProvider import org.eclipse.xtext.common.types.JvmType import org.eclipse.xtext.common.types.JvmIdentifiableElement class DomainmodelIdentifiableSimpleNameProvider extends IdentifiableSimpleNameProvider { def dispatch getSimpleName(JvmType element) { return "this"; } def dispatch getSimpleName(JvmIdentifiableElement element) { return super.getSimpleName(element); } }

83

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse. xtext.example.domainmodel/src/org/eclipse/xtext/example/domainmodel/jvmmodel/ DomainmodelIdentifiableSimpleNameProvider.xtend

112

LWC11 11.3.2 Qualied Name Provider Create the package org.xtext.example.domainmodel.naming


84

Submission

and

add

class

DomainmodelQualifiedNameProvider.java
1 2 3 4 5 6 7 8 9 10

package org.eclipse.xtext.example.domainmodel.naming; import org.eclipse.xtext.common.types.JvmGenericType; import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; import com.google.inject.Inject; public class DomainmodelQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider { @Inject private IQualifiedNameConverter converter; QualifiedName qualifiedName(JvmGenericType type) { return converter.toQualifiedName(type.getQualifiedName()); } }

11 12 13 14 15 16 17

11.3.3 Value Converter Create the package org.xtext.example.domainmodel.valueconverter and add a class DomainmodelValueConverterService.java:
1 2 3 4 5 6 7 8
84

85

package org.eclipse.xtext.example.domainmodel.valueconverter; import org.eclipse.xtext.conversion.IValueConverter; import org.eclipse.xtext.conversion.ValueConverter; import org.eclipse.xtext.xbase.conversion.XbaseValueConverterService; import com.google.inject.Singleton;

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse. xtext.example.domainmodel/src/org/eclipse/xtext/example/domainmodel/naming/ DomainmodelQualifiedNameProvider.java https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext. example.domainmodel/src/org/eclipse/xtext/example/domainmodel/valueconverter/ DomainmodelValueConverterService.java

85

113

LWC11 /** * Adds a value conversion for the QualifiedNameWithWildCard rule. */ @SuppressWarnings("restriction") @Singleton

Submission

9 10 11 12 13 14 15 16 17 18 19 20

public class DomainmodelValueConverterService extends XbaseValueConverterService { @ValueConverter(rule = "QualifiedNameWithWildCard") public IValueConverter<String> getQualifiedNameWithWildCard() { return getQualifiedNameValueConverter(); } }

11.3.4 Guice Binding The new custom classes must be bound to the
86

runtime :

module

now.

Open

DomainmodelRuntimeModule and set the following content


1 2 3 4

package org.eclipse.xtext.example.domainmodel; import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.example.domainmodel.jvmmodel. DomainmodelIdentifiableSimpleNameProvider; import org.eclipse.xtext.example.domainmodel.naming.DomainmodelQualifiedNameProvider; import org.eclipse.xtext.example.domainmodel.typing.DomainmodelTypeProvider; import org.eclipse.xtext.example.domainmodel.valueconverter.DomainmodelValueConverterService ; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.xbase.featurecalls.IdentifiableSimpleNameProvider; import org.eclipse.xtext.xbase.typing.ITypeProvider; /** * Use this class to register components to be used at runtime / without the Equinox extension registry. */ @SuppressWarnings("restriction") public class DomainmodelRuntimeModule extends org.eclipse.xtext.example.domainmodel. AbstractDomainmodelRuntimeModule { @Override public Class<? extends IValueConverterService> bindIValueConverterService() { return DomainmodelValueConverterService.class;
86

5 6 7

8 9 10 11 12 13

14 15 16

17 18 19

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/DomainmodelRuntimeModule.java

114

LWC11 } @Override

Submission

20 21 22 23

public Class<? extends IdentifiableSimpleNameProvider> bindIdentifiableSimpleNameProvider () { return DomainmodelIdentifiableSimpleNameProvider.class; } @Override public Class<? extends ITypeProvider> bindITypeProvider() { return DomainmodelTypeProvider.class; } @Override public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() { return DomainmodelQualifiedNameProvider.class; } }

24 25 26 27 28 29 30 31 32 33 34 35 36

11.3.5 Scoping Until now there was no need to customize the scoping for the Domainmodel DSL, but with introduction of operations it is important. We will replace the Java stub class DomainmodelScopeProvider by an Xtend class, so delete the le from the scoping package. Then create DomainmodelScopeProvider.xtend:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

package org.eclipse.xtext.example.domainmodel.scoping import org.eclipse.xtext.xbase.scoping.XbaseScopeProvider import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations import org.eclipse.xtext.scoping.IScope import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EReference import org.eclipse.xtext.example.domainmodel.domainmodel.Entity import org.eclipse.xtext.common.types.JvmType import org.eclipse.xtext.example.domainmodel.domainmodel.Operation import org.eclipse.xtext.scoping.impl.SimpleScope import org.eclipse.xtext.resource.EObjectDescription import java.util.Collections import org.eclipse.xtext.util.Strings import org.eclipse.xtext.common.types.JvmFormalParameter import org.eclipse.xtext.naming.QualifiedName import org.eclipse.xtext.scoping.impl.MapBasedScope

115

LWC11 import org.eclipse.xtext.resource.IEObjectDescription import org.eclipse.xtext.common.types.JvmDeclaredType import org.eclipse.xtext.EcoreUtil2 import com.google.inject.Inject class DomainmodelScopeProvider extends XbaseScopeProvider { @Inject extension IJvmModelAssociations associations

Submission

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

override IScope createLocalVarScope(EObject context, EReference reference, IScope parent, boolean includeCurrentBlock, int idx) { switch (context) { Entity : { val jvmType = getJvmType(context) if(jvmType != null) return new SimpleScope(parent, Collections::singleton(EObjectDescription::^create( XbaseScopeProvider::THIS, jvmType))) } Operation : { val descriptions = context.params.map(e | e.createIEObjectDescription()) return MapBasedScope::createScope( super.createLocalVarScope(context, reference, parent, includeCurrentBlock, idx), descriptions); } } return super.createLocalVarScope(context, reference, parent, includeCurrentBlock, idx) } def createIEObjectDescription(JvmFormalParameter jvmFormalParameter) { EObjectDescription::^create(QualifiedName::^create(jvmFormalParameter.name), jvmFormalParameter, null); } def JvmType getJvmType(Entity entity) { entity.jvmElements.filter(typeof(JvmType)).head } override JvmDeclaredType getContextType(EObject call) { if (call == null) return null val containerClass = EcoreUtil2::getContainerOfType(call, typeof(Entity)); if (containerClass != null) return getJvmType(containerClass) as JvmDeclaredType else return super.getContextType(call)

34 35 36 37 38

39 40 41 42 43 44 45

46 47 48 49 50 51 52 53 54 55 56 57 58 59

116

LWC11 } }

Submission

60 61

To avoid that the Xtext code generator would produce the Java stub class again, which would conict with the one which is compiled from the Xtend class to xtend-gen, the conguration of the workow must be changed. Open GenerateDomainmodelLanguage.mwe2 and change:
1 2 3

fragment = scoping.ImportNamespacesScopingFragment { generateStub = false }

11.3.6 Extending The Generator Adding a compiler for operations Also our code generator should know how to produce meaningful content for operations. In fact, we can produce even executable code! Xbase compiles down to Java, and we will make use of this for our operations. Xbase provides therefore the XbaseCompiler class, which we will extend. In package ...domainmodel.Generator create the class DomainmodelCompiler.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
87

87

package org.eclipse.xtext.example.domainmodel.generator; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.common.types.JvmFormalParameter; import org.eclipse.xtext.common.types.JvmGenericType; import org.eclipse.xtext.common.types.JvmOperation; import org.eclipse.xtext.example.domainmodel.domainmodel.Operation; import org.eclipse.xtext.xbase.XAbstractFeatureCall; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.compiler.IAppendable; import org.eclipse.xtext.xbase.compiler.ImportManager; import org.eclipse.xtext.xbase.compiler.StringBuilderBasedAppendable; import org.eclipse.xtext.xbase.compiler.XbaseCompiler; import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations; import com.google.inject.Inject;

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/generator/DomainmodelCompiler.java

117

LWC11 @SuppressWarnings("restriction") public class DomainmodelCompiler extends XbaseCompiler { @Inject private IJvmModelAssociations associations; public String compile(Operation operation, ImportManager importManager) {

Submission

20 21 22 23 24 25 26 27

StringBuilderBasedAppendable appendable = new StringBuilderBasedAppendable(importManager ); registerThis(operation, appendable); for(JvmFormalParameter param: operation.getParams()) { appendable.declareVariable(param, param.getName()); } return compile(operation.getBody(), appendable, operation.getType()).toString(); } protected void registerThis(Operation operation, StringBuilderBasedAppendable appendable) { Set<EObject> elements = associations.getJvmElements(operation); JvmOperation jvmOp = (JvmOperation) elements.iterator().next(); appendable.declareVariable(jvmOp.getDeclaringType(), "this"); } @Override protected boolean isVariableDeclarationRequired(XExpression expr, IAppendable b) { if (expr instanceof XAbstractFeatureCall && ((XAbstractFeatureCall)expr).getFeature() instanceof JvmGenericType) { return false; } return super.isVariableDeclarationRequired(expr,b); } @Override protected String getVarName(Object ex, IAppendable appendable) { if(ex instanceof JvmGenericType) { return "this"; } return super.getVarName(ex, appendable); } }

28 29 30 31 32 33 34 35

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

Adding functions to GeneratorExtensions The code generator will require some more extension functions. Replace the content of

118

LWC11 GeneratorExtensions.xtend by
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 88

Submission

package org.eclipse.xtext.example.domainmodel.generator import org.eclipse.xtext.example.domainmodel.* import org.eclipse.xtext.example.domainmodel.domainmodel.* import org.eclipse.emf.ecore.* import org.eclipse.xtext.common.types.* import java.util.Set import org.eclipse.xtext.xbase.compiler.* class GeneratorExtensions extends DomainmodelExtensions { def shortName(JvmTypeReference r, ImportManager importManager) { val builder = new StringBuilder() importManager.appendTypeRef(r, builder) builder.toString } def fileName(Entity e) { e.packageName.folderName + / + e.name + .java } def folderName(String javaPackageName) { if(javaPackageName != null) javaPackageName.replace(., /) else } def parameterList(Operation o, ImportManager importManager) { o.params.map(p| p.parameterType.shortName(importManager) + + p.name).join(, ) } def isInterface(JvmTypeReference typeRef) { (typeRef.type as JvmGenericType).isInterface } }

88

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/generator/GeneratorExtensions.xtend

119

LWC11 Changing DomainmodelGenerator.xtend Now open DomainmodelGenerator.xtend and set this content
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 89

Submission

package org.eclipse.xtext.example.domainmodel.generator import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IGenerator import org.eclipse.xtext.generator.IFileSystemAccess import org.eclipse.xtext.xbase.compiler.* import org.eclipse.xtext.xbase.* import org.eclipse.xtext.example.domainmodel.domainmodel.* import org.eclipse.xtext.common.types.* import java.util.* import com.google.inject.Inject import static extension org.eclipse.xtext.xtend2.lib.ResourceExtensions.* class DomainmodelGenerator implements IGenerator { @Inject extension GeneratorExtensions generatorExtensions @Inject DomainmodelCompiler domainmodelCompiler override void doGenerate(Resource resource, IFileSystemAccess fsa) { for(entity: resource.allContentsIterable.filter(typeof(Entity))) { fsa.generateFile(entity.fileName, entity.compile) } } def compile(Entity e) val importManager = new ImportManager(true) /* first evaluate the body in order to collect the used types for the import section */ val body = body(e, importManager) IF !(e.packageName.isNullOrEmpty) package e.packageName; ENDIF FOR i:importManager.imports import i; ENDFOR body
89

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/generator/DomainmodelGenerator. xtend

120

LWC11 -

Submission

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

def body(Entity e, ImportManager importManager) public class e.name e.superTypeClause(importManager){ FOR f:e.features feature(f, importManager) ENDFOR } def superTypeClause(Entity e, ImportManager importManager) { if(e.superType != null) (if (e.superType.isInterface) implements else extends ) + e.superType.shortName(importManager) + " " else "" } def dispatch feature(Property p, ImportManager importManager) private p.type.shortName(importManager) p.name; public p.type.shortName(importManager) getp.name.toFirstUpper() { return p.name; } public void setp.name.toFirstUpper(p.type.shortName(importManager) p.name) { this.p.name = p.name; } def dispatch feature(Operation o, ImportManager importManager) public o.type.shortName(importManager) o.name(o.parameterList(importManager)) { domainmodelCompiler.compile(o, importManager) } }

There are two major changes now: We will use an ImportManager to automatically organize the required imports for the class. It is instantiated in the compile() function and passed to all other functions as additonal argument. An instance of our new class DomainmodelCompiler is injected and used in the function feature(Operation o, ImportManager importManager).

121

LWC11 -

Submission

11.4 Testing The New Feature


It is time that we get paid o for all the eort. Start your runtime Eclipse instance, open Person.dmodel and enter this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 90

import java.util.Date import p2.Car package p1 { entity Person { name : String firstname : String birthdate : Date ownedCar : Car salary : java.math.BigDecimal employee_of : example.Company op getFullName () : String { firstname + " " + name } } }

Not even that you are able to dene the implementation of operations in the body - it gets even translated to executable Java code! Open the generated class Person.java in src-gen and you will see that there is now a getFullName() method91 :
1 2 3 4 5 6

public String getFullName() { String _operator_plus = StringExtensions.operator_plus(this.firstname, " "); String _operator_plus_1 = StringExtensions.operator_plus(_operator_plus, this.name); return _operator_plus_1; }

Play a bit around with by adding more operations and changing the implementation. The content assist will help you a lot with the implementation, since you might not be familiar with the Xbase language so far. We cannot describe the full extend of what is allows, it is just too powerful. The Eclipse Help contains the Xbase Language Reference, which you may consult for a detailed description. Another good way to learn about Xbase, together with the reference manual, is the Xbase tutorial project, which can be installed through the New Project Wizard.
90 91

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/lwc11-test/src/Person.dmodel It might be necessary to add the empty operation rst and save it, then add just rstname and save. The compiler integration is not completely stable yet.

122

LWC11 -

Submission

12 Phase 1.6 - Multiple generators


The code generator should be extended to create another target artifact. We will create an XML document for each Entity.

12.1 Extending The Code Generator


We want to keep the changes minimal. At rst an additional helper function in GeneratorExtensions.xtend is needed to compute the le name for the XML documents. Open the le and add this function:
1 2 3

def xmlFileName(Entity e) { e.packageName.folderName + "/" + e.name + .xml }

It is quite the same as the existing fileName() function, only the le extension diers. Now open DomainmodelGenerator.xtend and change doGenerate():
1 2 3 4 5 6

override void doGenerate(Resource resource, IFileSystemAccess fsa) { for(entity: resource.allContentsIterable.filter(typeof(Entity))) { fsa.generateFile(entity.fileName, entity.compile) fsa.generateFile(entity.xmlFileName, entity.compileXml) } }

This will call compileXml for each Entity, which you now add to the le92 :
1 2 3

def compileXml (Entity e) { <?xml version="1.0"?>

<e.nameIF e.superType!=null super="e.superType.simpleName"ENDIF> 4 FOR p:e.features.filter(typeof(Property))


5 6 7 8 9

<p.name type="p.type.simpleName"/> ENDFOR </e.name> }

92

Single quotes are not printed correctly in the document

123

LWC11 -

Submission

12.2 Testing The Changes


Restart the runtime Eclipse instance, open Car.dmodel, do a change and save it. The Builder runs the generator again and produces the XML le now:
1 2 3 4 5

<?xml version="1.0"?> <Car> <make type="String"/> <model type="String"/> </Car>

13 Phase 2 - Non Functional


Phase 2 is intended to show a couple of non-functional properties of the LWB.

13.1 Phase 2.1 - How to evolve the DSL without breaking existing models
Actually, we have seen this already several times during the tutorial. As long as the DSL changes do not introduce breaking concepts the DSL can be evolved without harming the existing models. Best examples here are the introduction of namespaces (Phase 1.4) or operations (Phase 1.5). The package declaration is optional, and before introduction of this concept all entities were virtually in the same namespace.

13.1.1 Adding rename element refactoring support Since the names of elements play an important role in resolving cross-references, a change of an elements name does break all references to an element. With Xtext 2.0 it is possible to add refactoring support for renaming model elements. The required code for this will be added to your implementation by adding the RefactorElementNameFragment to the generator workow GenerateDomainmodelLanguage.mwe2:
1

fragment = refactoring.RefactorElementNameFragment {}

This will add entries to the plugin.xml_gen le in the UI project. The le plugin.xml_gen is produced when the plugin.xml le already exists, since it might be manually edited and for

124

LWC11 -

Submission

XML les the Generation Gap Pattern cannot be realized. Thus it is intended to manually merge the generated parts into the plugin.xml le. As long as you did not make any customization to the plugin.xml le, you can safely remove it before running the generator. This will produce the le again with the latest content. Or you merge changes, which can be done best if you select both les in the Package Explorer and choose Compare With / Each Other from the context menu. Decide yourself which way you go and rerun the Xtext generator workow. After restarting the runtime workbench select an elements name in the Xtext editor, and open the context menu or press the CMD+SHIFT+R shortcut. There will be now an entry Rename Element. After choosing that you can enter the new name for the element.

After hitting enter all occurances of the element, and even derived compilation units (i.e. generated Java classes), will be renamed.

125

LWC11 -

Submission

13.2 Phase 2.2 - How to work with the models eciently in the team
Xtext models are plain text, so the obvious way to work with these models is to use a SCM system. Of course it does not matter which SCM tool you use for your team. You can use Xtext editor support even in the le compare dialog. Open the local history on one of your .dmodel les93 and double click in the History View on one of the older versions of the le. The compare dialog pops up
94

You can see the dierences between the two le versions clearly and even be able to use the Xtext editor for correcting your version (left side).

93 94

In the editor, open the context menu and choose Compare With / Local History If the text is not highlighted you are using the normal text comparison. Click the arrow right of Text Compare and choose Domainmodel Compare then.

126

LWC11 -

Submission

Since Xtext DSL les are EMF resources, you can even use EMF Compare95 to compare the le dierences.

13.3 Phase 2.3 - Demonstrate Scalability Of The Tools


13.3.1 Scalability of Xtext DSL UI Scalability is crucial for a language workbench, especially when it comes to integration into an IDE. Slow reaction on typing or other features like syntax coloring and content assist reduces the acceptance of the tooling. One reason why still many developers use tools like Emacs and VI is that is hell fast. And the same experience a user must have when working with textual DSLs, and having all the nice IDE features even on top without feeling the workbench slowing down. This is a big challange, especially when having a large number of resources and large les.

Xtexts history in regard to scalability Xtext was developed and improved over a long time and with high focus on scalability. It is used by a vast number of projects of all sizes with high requirements on on performance. Xtext 2.0 will actually be the 4th generation of Xtext:
95

http://www.eclipse.org/emf/compare/

127

LWC11 -

Submission

Originally Xtext was developed as part of the openArchitectureWare framework. It was nice for smaller projects, but did not scale for large projects. Also its architecture was not extensible and exible enough to realize better scalability. As a next step, Xtext moved to Eclipse as Incubation Project in the TMF96 umbrella project and was completely rewritten. Many performance bottlenecks were adressed and it the architecture was designed for extensibility and performance. Xtext 0.7.0 was released as part of the Eclipse Galileo Simultaneous Release in June 2009. Towards Eclipse Helios Xtext graduated from its Incubator status, allowing the version increment to 1.0.0. Again, much eort has been spent on scalability. Especially Xtexts Index was responsible for that. After the Eclipse Helios release in June 2010 there where 2 service releases 1.0.1 and 1.0.2, which again adressed heavily scalability. At the moment the nal preparations for the Xtext 2.0.0 release are done, which will be available in June 2011 with Eclipse Indigo. And guess, scalability is again one of the main points. This time this will be achieved by Xbase, an improved Index implementation, a clustered builder and lower memory footprint, to mention just some of the tasks.

Scalability measurements At EclipseCon 2011 the Xtext team presented some concrete measurements in their talk Whats cooking in Xtext 2.097 .

Build Time Test scenario: 4000 resources in 2 projects Xtext 1.0.0 (cancelled after several minutes) Xtext 1.0.2: 5.3 min Xtext 2.0.0: 1.2 min

Memory Consumption The part consuming most memory in Xtext is the so called Node model, which represents the textual nodes of each document. This is attached to the AST, which contains the semantic model information. For a 3 MB text le (this is just a test case, no one should create a single model le of that size
96 97

Textual Modeling Framework http://www.slideshare.net/schwurbel/whats-cooking-in-xtext-20

128

LWC11 -

Submission

really), this consumed in Xtext 1.0.0 780 MB memory. With Xtext 2.0.0 it was reduced by 85 percent to 120 MB.

14 Phase 3 - Freestyle
14.1 Creating A DSL Text Formatter
From the grammar Xtext cannot infer how a DSL model should be formatted meaningful, but it provides a convenient API for that. A base implementation for the formatter of the Domainmodel DSL has already been produced to class DomainmodelFormatter in the formatting package of the Runtime project. Open this le and add the implementation for the configureFormatting() method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 98

@Override protected void configureFormatting(FormattingConfig c) { DomainmodelGrammarAccess f = (DomainmodelGrammarAccess) getGrammarAccess(); c.setAutoLinewrap(120); c.setLinewrap(1, 2, 3).around(f.getAbstractElementRule()); c.setLinewrap(1, 2, 3).around(f.getPackageDeclarationRule()); c.setLinewrap(1, 1, 2).around(f.getFeatureRule()); List<Pair<Keyword,Keyword>> pairs = f.findKeywordPairs("{", "}"); for (Pair<Keyword, Keyword> pair : pairs) { c.setIndentation(pair.getFirst(), pair.getSecond()); } c.setLinewrap(0, 1, 2).before(f.getSL_COMMENTRule()); c.setLinewrap(0, 1, 2).before(f.getML_COMMENTRule()); c.setLinewrap(0, 1, 1).after(f.getML_COMMENTRule()); }

It will assure that a line wrap will be inserted around Entity, Package, Import , Property and Operation statements. Further it searches for curly braces in the grammar (which it will
98

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/formatting/DomainmodelFormatter. java

129

LWC11 -

Submission

nd for PackageDeclaration and Entity and Operations) and indent the content of the block statements. After restart of the runtime Eclipse instance you can now format a .dmodel model by pressing CTRL+F.

14.2 Headless Generator


Xtend based code generators that use Xtext models can be deployed as executable Jar from Eclipse. The basic proceeding was decribed by Sebastian Zarnekow in his Blog: How to Deploy Xtext to a Headless Plain-Java Environment?.

14.2.1 Create The Main Class In the generator package a Main class already exists, but we will extend it a little to make the command line interface more user friendly by the use of the Apache Commons CLI framework 99 . The main method of the Main class will take two required arguments -srcdir and -targetdir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
99

package org.eclipse.xtext.example.domainmodel.generator; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher; import com.google.common.collect.Lists; public class Main { /** The value of module in the .mwe2 file to execute */ private static final String WORKFLOW_MODULE = "org.eclipse.xtext.example.domainmodel.generator.DomainmodelGeneratorMWE"; public static void main(String[] args) { https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext.example. domainmodel/src/org/eclipse/xtext/example/domainmodel/generator/Main.java

130

LWC11 int retval = new Main().run(args); if (retval != 0) System.exit(retval); } @SuppressWarnings("static-access") protected int run (String[] args) { final Options options = new Options(); Option optSrcDir = OptionBuilder.withArgName("path").withDescription("Model source directory").hasArg() .isRequired().withValueSeparator( ).create("srcdir"); Option optTargetDir = OptionBuilder.withArgName("path").withDescription("Generator target directory (default: ./src-gen)").hasArg() .create("targetdir"); options.addOption(optSrcDir); options.addOption(optTargetDir); // create the parser final CommandLineParser parser = new GnuParser(); CommandLine line = null; try { line = parser.parse(options, args); } catch (final ParseException exp) { System.out.println(exp.getMessage()); final HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("java -jar dmodelgen.jar [OPTIONS]", options); return -1; } List<String> launchArgs = Lists.newArrayList(); launchArgs.add(WORKFLOW_MODULE); launchArgs.add("-pmodelPath=" + line.getOptionValue("srcdir")); launchArgs.add("-ptargetDir=" + line.getOptionValue("targetdir", "./src-gen")); try { Mwe2Launcher.main(launchArgs.toArray(new String[0])); return 0; } catch (Exception e) { return -1; } }

Submission

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

131

LWC11 }

Submission

67

14.2.2 Create A Launch Conguration For The Main Class Open the Run Congurations dialog from the Run menu, select Java Application and add a new one. You may change the Name: property of the Launch Conguration to something like Domainmodel Generator now. For Project select org.eclipse.xtext.example.domainmodel and as Main Class select org.eclipse.xtext.example.domainmodel.generator.Main.

Press Apply and Close now. We do not execute the Launch Cong, and anyway, we did not specify the necessary arguments srcdir and targetdir yet.

14.2.3 Export A Runnable Jar From the File menu select Export and from the Java section the option Runnable JAR le. Press Next. On the following wizard page you must select the previously dened Launch Conguration.

132

LWC11 Select the option Package required libraries into generated JAR.

Submission

Select an export destination le (dmodelgen.jar + path) and press Finish. This will create the Runnable Jar le now.

14.2.4 Executing The Generator Open a command line window. The generator is invoked with java -jar dmodelgen.jar -srcdir <srcdir> -targetdir <targetdir>. If you invoke the Jar without any option or with invalid options you will get a usage text.
1 2 3 4

>java -jar dmodelgen.jar usage: java dmodelgen.jar [OPTIONS] -srcdir <srcdir> Model source directory -targetdir <targetdir> Generator target directory

When you specify -srcdir and targetdir it will run the generator, scan <srcdir> and directory below for Xtext resources (in our case .entity and .instance les) and generate the code for it to <targetdir>.
1 2

java -jar ../../dmodelgen.jar -srcdir . -targetdir ../src-gen 0 INFO Workflow - Done.

133

LWC11 -

Submission

Note that all Java classes that are referred from .dmodel les must be packaged into the Jar, and thus would require a seperate project. We used in Person.dmodel a Company class which was dened in the same project and thus is not on the classpath of the Jar. You would experience a problem like this:
1

Person.dmodel - /Users/thoms/Development/workspaces/languageworkbench/lwc11-test/src /./Person.dmodel 10: Couldnt resolve reference to JvmType example.Company.

To keep things simple, just remove references to these Java classes now, or if you want to keep it, move this library entity to a seperate project and add a dependency of the DSL runtime project to it.
100

14.3 Creating A Code Generator For The Instance DSL


The LWC task only required the implementation of the Instance DSL with integration to the Domainmodel DSL. But the Instance DSL is nearly useless if we do not something with the models described in this language. Therefore we will add a code generator which will produce some Java classes for the models. A stub for the Xtend generator was already created by Xtext into the package org.xtext.lwc.instances.generator in the le InstancesGenerator.xtend. This class will need another one as extension, which will be added now.

14.3.1 ExpressionsCompiler.xtend Create the le ExpressionsCompiler.xtend with this content


1 2 3 4 5 6 7 8 9 101

package org.xtext.lwc.instances.generator import org.eclipse.xtext.xbase.compiler.XbaseCompiler import org.xtext.lwc.instances.instances.DateLiteral import org.eclipse.xtext.xbase.compiler.IAppendable import java.util.Date import java.util.Calendar import org.eclipse.xtext.xbase.XAbstractFeatureCall import org.eclipse.xtext.xbase.XVariableDeclaration
100 101

MaybethiswillbeimprovedinalaterimplementationoftheLWCprojects. https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/generator/ExpressionsCompiler.xtend

134

LWC11 -

Submission

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

class ExpressionsCompiler extends XbaseCompiler { def dispatch void toJavaStatement(DateLiteral expr, IAppendable b, boolean isReferenced) { val name = b.declareVariable(expr, _date) b.append( java.util.Date name = new java.util.Date(); name.setDate(expr.day); name.setMonth(expr.month); name.setYear(expr.year); ); } def dispatch void toJavaExpression(DateLiteral expr, IAppendable b) { b.append(b.getName(expr)) } override void appendFeatureCall(XAbstractFeatureCall call, IAppendable b) { switch varDecl : call.getFeature() { XVariableDeclaration : b.append("get"+ varDecl.name.toFirstUpper +"()") default : super.appendFeatureCall(call, b) } } }

14.3.2 InstancesGenerator.xtend Now the InstancesGenerator class can be implemented.


1 2 3 4 5 6 7 8 9 10 102

package org.xtext.lwc.instances.generator import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.generator.IGenerator import org.eclipse.xtext.generator.IFileSystemAccess import org.xtext.lwc.instances.instances.Instance import org.eclipse.xtext.xbase.XVariableDeclaration import org.eclipse.emf.common.util.EList import org.eclipse.xtext.xbase.XExpression import org.eclipse.xtext.xbase.compiler.StringBuilderBasedAppendable
102

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.xtext.lwc.instances/src/ org/xtext/lwc/instances/generator/InstancesGenerator.xtend

135

LWC11 import org.eclipse.xtext.xbase.XBlockExpression import org.xtext.lwc.instances.instances.ObjectLiteral import com.google.inject.Inject class InstancesGenerator implements IGenerator { @Inject ExpressionsCompiler compiler override void doGenerate(Resource resource, IFileSystemAccess fsa) { val instance = resource.contents.head as Instance val lastSegment = resource.URI.lastSegment val simpleFileName = lastSegment.substring(0, lastSegment.indexOf(.)) if (instance!=null) {

Submission

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

fsa.generateFile(simpleFileName+".java", generateJavaFile(instance, simpleFileName)) } } def generateJavaFile(Instance inst, String name) public class name { FOR varDecl : inst.expressions.filter(typeof(XVariableDeclaration)) private varDecl.type.identifier varDecl.name = null; public varDecl.type.identifier getvarDecl.name.toFirstUpper() { if (varDecl.name == null) { varDecl.name = new varDecl.type.identifier(); generateExpression(varDecl.right, varDecl.name) } return varDecl.name; } ENDFOR } def dispatch String generateExpression(XExpression expr, String name) { throw new IllegalArgumentException() } def dispatch String generateExpression(ObjectLiteral expr, String name) { val appendable = new StringBuilderBasedAppendable() appendable.declareVariable(expr, name) for (e : expr.expressions) { compiler.toJavaStatement(e, appendable, false) } return appendable.toString; } }

136

LWC11 -

Submission

After restarting the runtime workbench the generator will produce automatically the output les for .instance les if the project contains a src-gen folder. Our sample instance model
p1.Person p = { 2 name = "Voelter"
1 3 4 5 6 7 8 9 10 11

firstname = "Markus" birthdate = 14.02.1977 ownedCar = c } p2.Car c = { make = "VW" model = "Touran" }

will be compiled to a Sample.java class:


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public class Sample { private p1.Person p = null; public p1.Person getP() { if (p == null) { p = new p1.Person(); p.setName("Voelter"); p.setFirstname("Markus");java.util.Date _date = new java.util.Date(); _date.setDate(14); _date.setMonth(2); _date.setYear(1977); p.setBirthdate(_date); p2.Car _c = getC(); p.setOwnedCar(_c); } return p; } private p2.Car c = null; public p2.Car getC() { if (c == null) { c = new p2.Car(); c.setMake("VW");

137

LWC11 c.setModel("Touran"); } return c; } }

Submission

25 26 27 28 29

14.4 Tweaking The User Interface


The UI for the developed language is already very nice, but until now we have just experienced what Xtext produces from the grammar, without any customization. Most prominent part of the UI for the language is of course the editor, but there are also other UI parts that can be inuenced, like the outline view. In this section we will improve some features of the UI for the Domainmodel Language. Until now we modied just sources from the runtime project. All modications mentioned in this section will be done in the org.eclipse.xtext.example.domainmodel.ui project.

138

LWC11 14.4.1 Customizing The Outline View

Submission

If you open the Person.dmodel and expand the content tree of the Outline View103 If it is not open, open it with Window / Show View / Outline, you can see that the structure of the model is represented. But actually, from the provided content you cannot see which type of objects they represent, and some objects are just labeled <unnamed>.

This can be improved easily by customizing the Label Providers. You will nd two classes in the ...domainmodel.ui.labeling package: DomainmodelDescriptionLabelProvider and DomainmodelLabelProvider. If you open DomainmodelLabelProvider you will see that the Xtext generator already produced pseudocode on how to extend this class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class DomainmodelLabelProvider extends DefaultEObjectLabelProvider { @Inject public DomainmodelLabelProvider(AdapterFactoryLabelProvider delegate) { super(delegate); } /* //Labels and icons can be computed like this: String text(MyModel ele) { return "my "+ele.getName(); } String image(MyModel ele) { return "MyModel.gif";

139

LWC11 } */ }

Submission

16 17 18

So, to modify the label text for some type of the DSL metamodel, just add a method named text() which takes the specic type as argument. To get started, add this method:
1 2 3 4 5 6 7

public String text(Property property) { StringBuilder builder = new StringBuilder(); builder.append(notNull(property.getName())); builder.append(" : "); append(builder, property.getType()); return builder.toString(); }

Similar simple it is to add icons to the outlined elements. Icons make it easy to identify the type of objects in the DSL model. And it looks pretty much cooler. Of course the rst thing you need is a set of icon images. For our example you can download an archive from here. Create a folder named icons in the UI plugin and put the images into this folder. We could add a method to the Label Provider for each metatype like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public String image (PackageDeclaration object) { return "PackageDeclaration.gif"; } public String image (Entity object) { return "Entity.gif"; } public String image (Property object) { return "Property.gif"; } public String image (Operation object) { return "Operation.gif"; }

Or we can make it even simpler when - like here - the names of the icon images match the objects EClass, and just override the doGetImage() method. If we follow this naming convention the code becomes smaller and (very slightly) faster, since the Xtext framework does not have to use Polymorphic Dispatch. So just add (instead of the above code)
104

104

https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/doc/LWC11_Documentation/snippets/

140

LWC11 -

Submission

1 2 3 4 5 6 7

@Override protected Object doGetImage(Object element) { if (element instanceof EObject) { return ((EObject) element).eClass().getName() + ".gif"; } return super.doGetImage(element); }

Start your runtime workbench and look how the outline presentation has changed:

This has already improved the outline structure, but below Properties and Operations there are nodes for the contained JvmParametrizedTypeReference and XExpression objects. These elements add no value for the outline structure, and thus we want to remove them. Further we want to remove the root of the structure, which represents the DomainModel instance, and just present the content of the model. These customizations can be done in the Content Provider for the outline; more specic in the class DomainmodelOutlineTreeProvider, which can be found in the outline package. Add this content
1 2 3 4 5 6 7 8 9 10 105

@Override protected void _createChildren(DocumentRootNode parentNode, EObject rootElement) { for (EObject content : rootElement.eContents()) { createNode(parentNode, content); } } protected boolean _isLeaf(Feature feature) { return true; } phase3/DomainmodelLabelProvider.java https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext. example.domainmodel.ui/src/org/eclipse/xtext/example/domainmodel/ui/outline/ DomainmodelOutlineTreeProvider.java

105

141

LWC11 After restarting the runtime workbench the Outline View look much nicer now:

Submission

The labels could be further improved, e.g. adding the operation parameters and return type, show the super type of an Entity, show for array type modiers. This needs slightly more coding in the DomainmodelLabelProvider class, which we do not want in detail here, but which you can see in the linked source le. Not important during development, but later if you would like to publish the plugins, is to add the folder to the plugins build description. Open the build.properties le and in the opened Build Conguration form page check the icons folder in the Binary Build section.

142

LWC11 14.4.2 Quickxes

Submission

Quickxes are proposals that can be activated on resource markers (errors or warnings), which allow to manipulate the document to x the error situation. Remember the validation rule that Entity names should start with a capital name. If you provide a name starting with lowercase, a warning message is produced for the Entitys name. A Quickx could propose to capitalize the name. To implement a Quickx all necessary information to apply the Quickx must be provided on creation of the validation message. This is specically a unique identier for the error code (for which we introduced the IssueCodes interface) and data about the error context, in this case the Entity name. Since Quickxes can be also applied to a model also from the Problems View even when the editor is closed, the Quickx implementation has no access to the EObject in error and its context. The Quickx is applied to the Document, not by manipulating model objects. For implementing Quickxes a stub has already been created - DomainmodelQuickfixProvider. It already contains in comments how to implement the quickx roughly for capitalizing names, but it does not completely match out code. Instead, set the following implementation
1 2 3 4 5 6 7 106 107

package org.eclipse.xtext.example.domainmodel.ui.quickfix; public class DomainmodelQuickfixProvider extends DefaultQuickfixProvider { @Fix(IssueCodes.INVALID_TYPE_NAME) public void fixTypeName(final Issue issue, IssueResolutionAcceptor acceptor) { acceptor.accept(issue, "Capitalize name", "Capitalize name of " + issue.getData()[0] + "", "upcase.png", // exemplary textual modification new IModification() { public void apply(IModificationContext context) throws BadLocationException { IXtextDocument xtextDocument = context.getXtextDocument(); String firstLetter = xtextDocument.get(issue.getOffset(), 1); xtextDocument.replace(issue.getOffset(), 1, Strings.toFirstUpper(firstLetter)); } }); }

8 9 10 11 12 13 14 15 16 17

106 107

Import statements were removed in this text https://svn.codespot.com/a/eclipselabs.org/lwc11-xtext/trunk/org.eclipse.xtext. example.domainmodel.ui/src/org/eclipse/xtext/example/domainmodel/ui/quickfix/ DomainmodelQuickfixProvider.java

143

LWC11 @Fix(IssueCodes.INVALID_FEATURE_NAME)

Submission

18 19 20

public void fixFeatureName(final Issue issue, IssueResolutionAcceptor acceptor) { acceptor.accept(issue, "Uncapitalize name", "Uncapitalize name of " + issue.getData() [0] + "", "upcase.png", // exemplary semantic modification new ISemanticModification() { public void apply(EObject element, IModificationContext context) { ((Feature) element).setName(Strings.toFirstLower(issue.getData()[0])); } }); } @Fix(IssueCodes.MISSING_TYPE) public void createReferenceType(final Issue issue, IssueResolutionAcceptor acceptor) { createLinkingIssueResolutions(issue, acceptor); } }

21 22 23 24 25 26 27 28 29 30 31 32 33 34

Restart the runtime workbench now and change the name Person to person. The warning message appears and you will get also a Quickx proposal in the tooltip:

Clicking on Capitalize name will correct the name.

144

You might also like