LWC13 XtextSubmission
LWC13 XtextSubmission
Xtext Submission
Version: 1.0 - April 08th 2013
Karsten Thoms, Johannes Dicks, Thomas Kutz (itemis)
LWC13 - Submission
Abstract
The Language Workbench Challenge 2013 (LWC13) is an initiative created by a group of experts
at the CodeGeneration 2010 conference
1
. The aim is to set a common task
2
for Language
Workbenches
3
which is implemented with the dierent existing alternatives in a comparable
way.
This document describes in detail how the task is solved with Xtext
4
. Xtext is one of the most
well known Language Workbenches and part of the Eclipse Modeling Project
5
.
Testimonial
We would like to thank:
1. Angelo Hulshout for initiating and organizing the Language Workbench Challenge. It is
his work that allows the Language Workbench Challenge to continue now in its 3rd year.
2. The Xtext Team is doing a great job on developing a robust, exible and easy to use
Language Workbench.
3. Gregor Kurpiel is a Web and Mobile Developer at itemis AG and supported us on creating
a basic web application style.
1
http://www.codegeneration.net/cg2010/
2
see http://www.languageworkbenches.net/ for the detailed description of the LWC11 competition and
other submissions
3
http://martinfowler.com/articles/languageWorkbench.html, http://blog.efftinge.de/2007/11/
definition-of-term-language-workbench.html
4
http://www.xtext.org
5
http://www.eclipse.org/modeling
2
LWC13 - Submission
Document History
Version 0.1 - 2013-01-28
Initial creation
Version 1.0 - 2013-04-08
Final version for the Language Workbench Challenge workshop in Cambridge
3
LWC13 - Submission
Table Of Contents
1 Introduction 6
1.1 Task Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Technology Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Installing Eclipse and Xtext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4 Workspace Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Workspace Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Launch Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2 Developing the Questionnaire Language 9
2.1 Xtext Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Create the DSL Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Dening the Grammar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Generate Language Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 17
Runtime Project - folder src . . . . . . . . . . . . . . . . . . . . . . . . . 18
Runtime Project - folder src-gen . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Testing the Questionnaire Language . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5.1 Creating a Launch Conguration . . . . . . . . . . . . . . . . . . . . . . 19
2.5.2 Create Test Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.6 Xbase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.7 Including Expressions into the QL Language . . . . . . . . . . . . . . . . . . . . 24
2.8 JVM Model Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.9 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3 Developing the Code Generator 35
3.1 Reference Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.1.1 IDE Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Webtools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Tomcat installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.1.2 Import and run reference . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Import project from git . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Download zip from project homepage . . . . . . . . . . . . . . . . . . . . 37
3.1.3 Main Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.1.4 Reference Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Bean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4
LWC13 - Submission
3.2 Xtend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.3 Code Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.1 Dispatcher template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.3.2 Output Conguration Provider . . . . . . . . . . . . . . . . . . . . . . . 48
3.3.3 JSF Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
JSF Form index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.4 Testing the Questionnaire Application . . . . . . . . . . . . . . . . . . . . . . . 56
4 Layout and Styling Language (QLS) 58
4.1 The Language QLS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.2 QLS Code Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5 Additional Concepts 69
5.1 Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.1.1 Extending the Java Validator class . . . . . . . . . . . . . . . . . . . . . 69
5.1.2 Constraint: Ensure order of questions . . . . . . . . . . . . . . . . . . . . 70
5.1.3 Constraint: Type conformance check . . . . . . . . . . . . . . . . . . . . 71
5.1.4 Testing validation rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.2 Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2.1 settings.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2.2 Parent POM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.3 Reactor POM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.2.4 QL Runtime Project POM . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.2.5 QL UI Project POM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.2.6 SDK Feature POM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.2.7 p2 Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2.8 Continuous Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6 Closing Words 87
5
LWC13 - Submission
1 Introduction
1.1 Task Description
The LWC13 task is to implement a DSL for questionnaires (Questionnaire Language, QL),
which basically allows the denition of forms with questions.
We assume that you have read the LWC13 assigment document carefully before continuing
reading this document.
1.2 Technology Stack
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
:-)
Grammar Denition
We will use Xtext 2.4.0, which is at the moment of writing the latest ocial release. Xtext 2.5
is in preparation and will be released with Eclipse Kepler in June 2013
6
. The solution approach
described here would work also with any version of Xtext >= 2.0, but the API might dier
slightly, so there is no guarantee that each codeline printed here would work exactly with all
versions. For better reproduction it is highly recommended to use the versions mentioned above.
Code Generator
For Code Generation we will use the language Xtend, which itself is based on Xtext. Xtend
makes use of a common expression language shipped with Xtext called Xbase. The languages
developed here will also be based on Xbase, but more on this later.
6
http://wiki.eclipse.org/Kepler/Simultaneous_Release_Plan
6
LWC13 - Submission
Questionnaire Application
The developed code generator will generate JavaServer Faces 2.1 (JSF)
7
pages in XHTML
le format. JSF is part of the Java Enterprise Edition (Java EE). It is useful to have a basic
understanding of how web applications work even if JSF provides a nice level of abstraction. The
JSF reference implementation from Oracle Mojarra 2.1.6
8
is able to run within the well known
Servlet container Apache Tomcat( v7.0)
9
. In order to not reload the whole page whenever some
content needs to be updated (e.g. optional questions need to be displayed depending on other
questions answers) we will use AJAX. The following screenshot shows the resulting application:
7
http://www.javaserverfaces.org/
8
http://javaserverfaces.java.net/
9
http://tomcat.apache.org/
7
LWC13 - Submission
1.3 Installing Eclipse and Xtext
Xtext is a SDK 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 pre-
packaged 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 4.2.x (Juno) including Xtext
2.3.1 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 4.3.0 Kepler, but be aware that this is not nalized until end of June 2013.
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) Windows 64 bit
e) Windows 32 bit
3. Unpack the downloaded archive le in a directory of your choice.
Example (Linux):
1 cd /opt/local
2 gzip -dc /download/eclipse-SDK-4.2-Xtext-2.3.1-linux-gtk-x86_64.tar.gz | tar
3 xvfp -
The archive will be extracted to a new directory named eclipse. Before unpacking the ar-
chive, please ensure that there is no subdirectory named eclipse yet! Dierent operating
systems may require dierent unpacking methods.
10
4. Start Eclipse by running the eclipse executable in the newly-created eclipse directory.
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
10
On Windows do not unpack it into a deep directory, since this might cause troubles with long path names.
8
LWC13 - Submission
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 ISO-
8859-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 Developing the Questionnaire Language
2.1 Xtext Overview
This overview will give you a rough idea about what Xtext
11
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
11
http://www.xtext.org
9
LWC13 - Submission
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.
The aforementioned example DSL 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:
1 entity Person {
2 name : String
3 gender : m
4 birthday : Date
5 }
6
7 entity Book {
8 title: String
9 authors: Person[]
10 isbnNumber: String
11 }
10
LWC13 - Submission
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, over-
view 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
detail how to work with Xtext.
2.2 Create the DSL Projects
Lets start creating the projects for the Questionnaire DSL. Open the New Project Wizard with
File / New / Project. Choose Xtext Project and press Next.
11
LWC13 - Submission
On the project wizard page enter:
1. Project name: org.eclipse.xtext.example.ql. Xtext will create multiple projects, which
share this prex. It is a convention to use a lowercase, dot-separated name.
2. Language name: org.eclipse.xtext.example.ql.QlDsl. This is an identier for the
language, which must be unique and follows a Java full qualied identier name pattern.
3. Language Extensions: ql. This will be the le extension for DSL les.
4. Uncheck the option Create SDK feature project. It would not harm to have that checked,
it would just create an additional Feature Project, which we do not handle in this tutorial
any further.
Now press Finish. Xtext will generate for you 3 projects into your workspace:
12
LWC13 - Submission
org.eclipse.xtext.example.ql: This is the Runtime Project, which holds the language
denition and any implementation which is not UI dependent. Most of the implementation
details of this tutorial will be done in this project.
org.eclipse.xtext.example.ql.tests: This project is intended to hold test code for
the language. Tests are implemented with JUnit. Xtext will generate some infrastructure
code required for tests into here. We will deal testing of DSLs at the end of this tutorial.
For now, you can close this project if you want.
org.eclipse.xtext.example.ql.ui: Xtext produces a language specic text editor. The
editor is an Eclipse plugin. While the runtime part of the language could be used in any
UI or even from command-line, the Editor is dependent on the Eclipse platform.
All projects are almost empty right now. Only the Runtime Project contains two important
les in the /src folder.
GenerateQlDsl.mwe2: This is a so-called MWE2 Workow. MWE is short for Modeling
Workow Engine, which is a framework that is intended to dene processes for code
generation
12
. This le denes the process to generate code for the DSL implementation.
QlDsl.xtext: This is the le that contains the DSL language denition itself. It is called
the Grammar of the language.
12
More about MWE2 see http://www.eclipse.org/Xtext/documentation.html#MWE2
13
LWC13 - Submission
2.3 Dening the Grammar
Open the Grammar le, QlDsl.xtext. In a rst step, we will leave out the expression part in
the syntax for simplicity. Enter the following text into the Grammar le
13
:
1 grammar org.eclipse.xtext.example.ql.QlDsl with org.eclipse.xtext.xbase.Xbase
2
3 generate qlDsl "http://www.eclipse.org/xtext/example/ql/QlDsl"
4
5 /* The top-most container of QL files is a Questionnaire */
6 Questionnaire:
7 imports+=Import*
8 forms+=Form*;
9
10 /* Allows importing of qualified names of types */
11 Import:
12 import importedNamespace=QualifiedName;
13
14 /* QL consists of questions grouped in a top-level form construct. */
15 Form:
16 "form" name=ID "{"
17 element += FormElement*
18 "}";
19
20 /* Abstract rule for elements contained in a Form */
21 FormElement:
22 Question
23 ;
24
25 /**
26 * - Each question identified by a name that at the same time represents the result of the
question.
27 * - A question has a label that contains the actual question text presented to the user.
28 * - Every question has a type.
29 */
30 Question:
31 name=ID ":" label=STRING type=JvmTypeReference
32 ;
With the grammar above, the QL language wont fulll all requirements of the LWC2013 task.
We will extend the grammar later to meet all requirements. With this grammar a valid model
le would look like this:
13
https://gist.github.com/kthoms/4758255
14
LWC13 - Submission
1 import types.Money
2
3 form Box1HouseOwning {
4 hasSoldHouse: "Did you sell a house in 2010?" boolean
5 hasBoughtHouse: "Did you by a house in 2010?" boolean
6 hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean
7
8 sellingPrice: "Price the house was sold for :" Money
9 privateDebt: "Private debts for the sold house: " Money
10 valueResidue: "Value residue: " Money
11 }
Now let us explain the grammar in more detail:
1 grammar org.eclipse.xtext.example.ql.QlDsl with org.eclipse.xtext.xbase.Xbase
The grammar has a unique identier named org.eclipse.xtext.example.ql.QlDsl
14
. It is
derived from another grammar, org.eclipse.xtext.xbase.Xbase. Xbase denes a grammar
for expressions, but more on this later. Xtext supports single inheritance for grammars.
1 generate qlDsl "http://www.eclipse.org/xtext/example/ql/QlDsl"
This is an instruction for the metamodel used for the language. The generate statement means
that Xtext generates an Ecore metamodel for this grammar
15
. The metamodel will represent
the languages Abstract Syntax Tree (AST). Xtext creates the following structure in the Ecore
metamodel:
an EPackage for each generate statement. The name of the EPackage is the rst argu-
ment (qlDsl), the packages nsURI is the second argument
("http://www.eclipse.org/xtext/example/ql/QlDsl").
an EClass
for each return type of a parser rule. If a parser rule does not dene a return type,
an implicit one with the same name as the rule itself is assumed. You can specify
multiple rules that return the same type but only one EClass will be generated.
for each type dened in an action or a cross-reference.
an EEnum
14
Thats what has been entered in the project wizard
15
http://www.eclipse.org/Xtext/documentation.html#metamodelInference
15
LWC13 - Submission
for each return type of an enum rule.
an EDataType
for each return type of a terminal rule or a data type rule.
Alternatively an Xtext grammar could be mapped to an existing Ecore metamodel
16
.
1 Questionnaire:
2 imports+=Import*
3 forms+=Form*;
The top-most container rule is Questionnaire. Per model resource exactly one instance of this
type will be contained in the root content of the resource. Any other element will be contained
directly or indirectly within this instance.
Each QL model will contain zero to many import statements, e.g.:
1 import java.math.BigDecimal
2 import types.Money
We will use them to import types used as a questions answer type. The += operator means,
that a to-many containment reference with name imports is added as EReference to the
Questionnaire EClass. The * means that this rule can be repeated zero to many times.
17
After the import statements, the QL model can contain multiple form declarations.
1 Import:
2 import importedNamespace=QualifiedName;
The Import rule is dened to start with the keyword import, followed by a QualifiedName.
The QualifiedName rule is not dened in the QlDsl.xtext grammar itself, it is inherited from
the Xbase grammar. This rule denes a so-called Datatype Rule, which maps to datatype, in
this case EString.
1 Import:
2 import importedNamespace=QualifiedName;
After the imports section QL forms are dened:
1 Form:
2 "form" name=ID "{"
3 element += FormElement*
4 "}";
16
http://www.eclipse.org/Xtext/documentation.html#grammarMixins
17
To enforce at least one rule call, the + operator would be used instead.
16
LWC13 - Submission
Forms have an attribute called name. ID is a terminal rule, which is dened in Xtexts root
grammar Terminals. It allows typical Java-style identiers (beginning with a word character
followed by arbitrary many characters, numbers or underscores).
The next step is to dene the rule FormElement. It is an abstract rule which will collect the
dierent alternatives of elements that can be contained in a form. In our rst step, the rule
Question will be the only alternative. We will introduce a second alternative later in the
grammar, and in order to reduce the refactoring eort we are introducing the FormElement
already now.
1 FormElement:
2 Question
3 ;
Finally, the Question rule is dened. In a rst step, Questions are identied by a name, followed
by a label string and a reference to a type. Later we will add expressions to compute the value.
1 Question:
2 name=ID ":" label=STRING type=JvmTypeReference
3 ;
A Questions type is a reference to a JVM Type. Think of this for now that we refer to Java
types. The JvmTypeReference rule is also inherited through Xbase, actually Xbase derives
itself from another grammar, Xtype, which declares these rules.
2.4 Generate Language Implementation
Now that the initial grammar of the language has been dened it is time to test the language.
Xtext ships with a code generator which generates all the glue code needed for the language
implementation.
To generate the code, we need to execute the generator workow GenerateQlDsl.mwe2. For
this, select the workow le, open the context menu and select Run As / MWE2 Workow.
The generator will print some information to the Console, and nally it should print Done..
1 0 [main] INFO lipse.emf.mwe.utils.StandaloneSetup - Registering platform
2 uri ....
3 ...
4 13727 [main] INFO .emf.mwe2.runtime.workflow.Workflow - Done.
17
LWC13 - Submission
After successful execution the projects will be lled with implementation code. Code that will
be regenerated each time the generator is executed will go to the source folder /src-gen (in
all three projects), whereas code generated to /src will be generated only once as skeleton. It
is safe to edit these classes.
Xtext follows the Generation Gap Pattern
18
: Generated code is based on the Xtext API. Manual
code is separated from generated code. Often manual classes are derived from generated classes
to allow overriding of generated code or adding functionality.
Investigate the generated code a bit. Some pieces to mention:
Runtime Project - folder src
The class QlDslRuntimeModule is a Guice conguration. Guice
19
is a famous Dependency
Injection
20
framework in Java. Xtext makes heavy use of Dependency Injection, which
in turn allows to exchange nearly every bit of the framework for customizing or to work
around limitations, if necessary, without the need to change the framework itself.
Class QlDslStandaloneSetup is needed when using the language in standalone mode,
i.e. without an Eclipse environment. Eclipse plugins, like Xtext and the language plu-
gin, usually need an OSGi container as execution environment. Xtext is designed to be
executable without the need to be deployed into an OSGi container, but for this certain
registrations are required which an OSGi container would usually provide automatically.
This is especially useful when Xtext based languages are used in build environments or
other IDEs.
Class QlDslFormatter allows the implementation of a declarative code formatter for the
DSL.
File QlDslJvmModelInferrer.xtend is a class implemented with the Xtend language.
The JVM Model Inferrer will play an important role later when we introduce expressions
and code generation.
Class QlDslJavaValidation allows the implementation of validation rules for the DSL.
Runtime Project - folder src-gen
18
http://heikobehrens.net/2009/04/23/generation-gap-pattern/
19
http://code.google.com/p/google-guice/
20
http://en.wikipedia.org/wiki/Dependency_injection
18
LWC13 - Submission
The Ecore metamodel is generated to le QlDsl.ecore.
The Java implementation code for the metamodel can be found in the package
org.eclipse.xtext.example.ql.qlDsl.
The package org.eclipse.xtext.example.ql.parser.antlr.internal contains an
ANTLR3
21
grammar and the Lexer and Parser classes generated from it.
2.5 Testing the Questionnaire Language
2.5.1 Creating a Launch Conguration
In order to test the language and the editor we need to deploy the developed plugins within
another Eclipse instance. For testing the easiest way is start a so-called Runtime Instance.
Open the dialog Run / Run Congurations and select the node Eclipse Application from the
left tree widget and press the icon with the + sign to create a new Launch Cong.
You could leave the defaults here or change the name and location like in the screenshot.
21
http://www.antlr.org
19
LWC13 - Submission
Now switch to the Arguments page and enter in the VM arguments text box:
1 -Xms40m -Xmx512m -XX:MaxPermSize=150m
Especially important is the MaxPermSize setting, since the default size of the PermGen space
of the VM (64MB) often is not enough.
Now press the Run button. Another Eclipse instance will start with an empty workspace.
Close the Welcome window.
2.5.2 Create Test Project
In the Runtime Workspace create a new Plug-in Project with name QLTest.
22
22
As before, uncheck the options on the second wizard page.
20
LWC13 - Submission
The DSL has to support custom datatypes like Money, which must be dened. Select the /src
folder and create a new Java class Money in package types:
1 package types;
2
3 import java.math.BigDecimal;
4
5 public class Money {
6 private BigDecimal amount;
7
8 public Money(BigDecimal amount) {
9 this.amount = amount;
10 }
11
12 public BigDecimal getAmount() {
13 return amount;
14 }
15 }
Select the /src folder and create a new le housepurchase.ql. Once you have created the
le a popup dialog will appear to ask, if you would like to add the Xtext nature on this project.
Answer with Yes.
From now on your project will be considered to contain les that Xtext should recognize (.ql
les). Projects having the Xtext nature will be processed by the Xtext Builder when building
projects, other projects are ignored. The Xtext Builder indexes the Xtext based resources, links
the cross-references in the editor, and validates the model les. On errors, resource markers are
created which can be seen in the editor and the Problems View.
Enter the content for housepurchase.ql
23
:
1 import types.Money
2
3 form Box1HouseOwning {
4 hasSoldHouse: "Did you sell a house in 2010?" boolean
5 hasBoughtHouse: "Did you by a house in 2010?" boolean
6 hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean
23
https://gist.github.com/kthoms/5036304
21
LWC13 - Submission
7
8 sellingPrice: "Price the house was sold for :" Money
9 privateDebt: "Private debts for the sold house: " Money
10 valueResidue: "Value residue: " Money
11 }
You see now that the editor has recognized our DSL. The languages keywords are highlighted.
Xtext oers far more than just syntax coloring for the language, it created a fully integrated
editor. You may explore some of the features now.
If you make errors, error markers are created and resolved while you type.
Content assist is oered with CTRL+SPACE.
The Outline view
24
presents the structure of the document, and allows quick navigation.
F3 allows jumping to the denition of an element, which is dened somewhere else. You
could try this by selecting Money and press F3. At the moment, only the type infor-
mation of questions is cross-referenced.
From now on, we will extend the DSL a bit further. This usually requires to restart the test
environment. So close it and proceed reading.
2.6 Xbase
The language developed in section 2.2 does not yet meet all demands on the LWC2013 task.
Two core features are missing: First, a questions answer can be computed, i.e. its answer can be
derived from an expression referring to previous questions answers. Second, questions can be
optional depending on the previous answers. For this, also the possibility to dene expressions
is needed. This is where Xbase comes into play.
Xbase is an expression language which can be reused in your own Xtext DSL. Its language
concepts are similar to Java, but with some syntactical derivations improving readability. The
Xbase grammar is dened in Xtext, thus its elements can be used in any other Xtext grammar
by importing or directly extending Xbase via Xtexts possibility for grammar inheritance. In
addition to the grammar, Xbase ships with further infrastructural parts like a compiler, inter-
preter, linker or static analyzer which all can be adapted to your own needs. In the background,
Xbase produces plain Java code which is run on the JVM. Like other DSLs dened with Xtext,
24
if not present, open with Window / Show View / Outline
22
LWC13 - Submission
Xbase provides also editor features like syntax highlighting, content assistance and navigation
via hyperlinks. In the following we will rst introduce some language concepts of Xbase, and
afterwards we will describe how to integrate Xbase into the Questionnaire DSL.
In Xbase everything is an expression which always has a return type which might be null
for some expressions. Variables are dened with the var keyword, whereas for constant values
the val keyword is used. Types are derived automatically, so they dont need to be dened
explicitly:
1 var myVariable = some modifiable value
2 val Integer myConstant = 42
Xbase ships with a library extending existing Java types like String or Integer with further
functionality. So besides the already known String operations from Java like toUpperCase or
toLowerCase, in Xbase expressions you can also use toFirstUpper and toFirstLower changing
only the rst letters case which might come in handy in some situations. Large numbers can
be written more readable by using underscores to separate digits:
1 "a day has ".toFirstUpper() + 86_400_000 + " milliseconds."
2 // results in: A day has 86400000 milliseconds.
As in Java, Xbase provides if-else-expressions for dening conditions. Since each expression
has a return type, it is valid to use if-else-blocks similar to the ternary operator in Java:
1 var x = if (condition) 42 else 43
There are further concepts in Xbase which we will not cover here in more detail, since they have
not much relevance for the Questionnaire language. So e.g. it is possible to use loops for iterating
over a collection of element; there is a switch-case-expression with type guards allowing for
dening behavior depending on the type of a parameter; and last but not least, Xbase allows
the denition of closures. For more details, please look up the reference documentation
25
or the
Xbase tutorials directly in Eclipse (File / New / Other.. / Xbase Tutorial ).
With these capabilities integrated in the Questionnaire language it is feasible to dene complex
domain logic e.g. for the result of a questionnaire directly in its denition. For example, when
designing a questionnaire for a test, lets say to dene a persons stress level, you can write
some Xbase code as expression for the last result question:
1 stressLevelResult: "Your Stress-Level: " String (
2 {
3 var Integer stressPoints = if (hasTimePressureAtWork) 30 else 0
25
http://www.eclipse.org/Xtext/documentation.html#xbaseLanguageRef_Introduction
23
LWC13 - Submission
4 stressPoints = stressPoints + daysSleepingBadPerWeek * 3
5 stressPoints = stressPoints + glassesOfAlcoholPerDay * 12
6 stressPoints = stressPoints - daysWithSportPerWeek * 2
7 if (stressPoints>80) "High" else if (stressPoints>40) "Medium" else "Low"
8 }
9 )
2.7 Including Expressions into the QL Language
Recall the example of a housowning questionnaire as mentioned in the LWC13 task:
1 import types.Money
2
3 form Box1HouseOwning {
4 hasSoldHouse: "Did you sell a house in 2010?" boolean
5 hasBoughtHouse: "Did you by a house in 2010?" boolean
6 hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean
7
8 if (hasSoldHouse) {
9 sellingPrice: "Price the house was sold for: " Money
10 privateDebt: "Private debts for the sold house: " Money
11 valueResidue: "Value residue: " Money (sellingPrice - privateDebt)
12 }
13 }
Compared to the language developed in section 2.2, we need to add (1) a condition statement
to express optional questions (see line 8) and (2) the capability for automatically deriving a
questions answer from previously answered questions (see line 11). As explained in section 2.2,
our grammar inherits from Xbase making its rules reusable in the questionnaire language. To
fulll the missing requirements our grammar needs to be extended to the following:
26
1 grammar org.eclipse.xtext.example.ql.QlDsl with org.eclipse.xtext.xbase.Xbase
2
3 generate qlDsl "http://www.eclipse.org/xtext/example/ql/QlDsl"
4
5 /* The top-most container of QL files is a Questionnaire */
6 Questionnaire:
7 imports+=Import*
8 forms+=Form*;
9
10 /* Allows importing of qualified names of types */
26
https://gist.github.com/kthoms/5114439
24
LWC13 - Submission
11 Import:
12 import importedNamespace=QualifiedName;
13
14 /* QL consists of questions grouped in a top-level form construct. */
15 Form:
16 "form" name=ID "{"
17 element += FormElement*
18 "}";
19
20 /* Abstract rule for elements contained in a Form */
21 FormElement:
22 Question | ConditionalQuestionGroup
23 ;
24
25 /**
26 * - Each question identified by a name that at the same time represents the result of the
question.
27 * - A question has a label that contains the actual question text presented to the user.
28 * - Every question has a type.
29 * - A question can optionally be associated to an expression:
30 * this makes the question computed
31 */
32 Question:
33 name=ID ":" label=STRING type=JvmTypeReference expression=XParenthesizedExpression?
34 ;
35
36 /**
37 * Groups questions within a block, optionally made conditional with an if-condition.
38 */
39 ConditionalQuestionGroup: {ConditionalQuestionGroup}
40 ("if" condition=XParenthesizedExpression)? "{"
41 element += FormElement*
42 "}"
43 ;
Compared to the grammar dened in section 2.2, the follwoing points have changed:
1 FormElement:
2 Question | ConditionalQuestionGroup
3 ;
A FormElement is now either a normal question or a ConditionalQuestionGroup. Conditional
question groups are groups of form elements embraced by an optional if-condition:
1 ConditionalQuestionGroup: {ConditionalQuestionGroup}
25
LWC13 - Submission
2 ("if" condition=XParenthesizedExpression)? "{"
3 element += FormElement*
4 "}"
5 ;
For the condition of the if-statement the grammar rule XParenthesizedExpression inherited
from Xbase is used. An XParenthesizedExpression is simply an expression in parenthesis.
The if-statement is optional (as dened by the question mark ? symbol) which allows for
just grouping questions without the necessarity for a condition. The inner elements are again
FormElements, making it possible to nest groups within groups and so on. The last part that
has changed is the Question rule. Here again the rule XParenthesizedExpression is used to
optionally embed Xbase expressions:
1 Question:
2 name=ID ":" label=STRING type=JvmTypeReference expression=XParenthesizedExpression?
3 ;
After changing the grammar, the implementation has to be regenerated. Run the
GenerateQlDsl.mwe2 workow again. Then restart the runtime workbench.
27
Xbase comes out of the box with the support for standard Java types like Strings or Integers
inside expressions. However, in the questionnaire language own data types, like the Money type
from the example, need also to be integrated. Such data types will be typically dened in a
Java class. When importing such a type via the import statement, it will be available in the
questionnaire denition. Xbase needs to know how to handle these types when they are used
in expressions with operators like +. -, * and /. The logic for these operators need to
be implemented in special methods in the data type itself. As example, lets see how this is
achieved for the Money type:
28
1 package types;
2
3 import java.math.BigDecimal;
4
5 public class Money {
6 private BigDecimal amount;
7
8 public Money (BigDecimal amount) {
9 this.amount = amount;
27
Select it from the Run / Run Congurations dialog or from the drop down menu next to the green play
button in the tool bar.
28
http://code.google.com/a/eclipselabs.org/p/lwc13-xtext/source/browse/examples/QLTest/src/
types/Money.java
26
LWC13 - Submission
10 }
11 public BigDecimal getAmount() {
12 return amount;
13 }
14
15 // Implement operators
16 public Money operator_minus (Money other) {
17 return new Money(this.amount.subtract(other.amount));
18 }
19 public Money operator_plus (Money other) {
20 return new Money(this.amount.add(other.amount));
21 }
22 public Money operator_multiply (Money other) {
23 return new Money(this.amount.multiply(other.amount));
24 }
25 public Money operator_divide (Money other) {
26 return new Money(this.amount.divide(other.amount));
27 }
28 }
The data type Money simply holds the amount as a value of type BigDecimal. For each operator
a special method, e.g. operator_minus(Money other), denes how to procede when this ope-
rator is used two values of type Money. In this simple example, a new Money object is created
and its value is computed corresponding to the operator type. When evaluating an expression,
Xbase searches for these methods inside the used types to compute the result.
In order to test the new version of the questionnaire language, the MWE workow needs to
be executed again (Right click on GenerateQlDsl.mwe2 / Run As.. / MWE2 Workow). The
questionnaire language now supports expressions, but there is still one point missing: Questions
cannot be referenced within an expression. For this, we need to derive a JVM model from the
questionnaire model which we will discuss in the next section.
2.8 JVM Model Inference
For languages using Xbase it is necessary to tell Xtext, how to map concepts of a language to
a Java model. In our example, a Form could be mapped to the Type concept, while Questions
are the elds of a class. By doing this, elements of the language can be made available in
expressions. Further, it allows that model elements are linkable where Java types are expected,
without necessarily generate a Java class.
27
LWC13 - Submission
The derivation of the Java model for language concepts is the responsibility of the JVM Mo-
del Inferrer, which is a class that implements the IJvmModelInferrer interface. A skeleton
has already been generated into package org.eclipse.xtext.example.ql.jvmmodel. The le
QlDslJvmModelInferrer.xtend is a class written with Xtend.
The mapping that has to be implemented for the Questionnaire DSL should be as follows:
1. Each Form instance is mapped to a JvmDeclaredType (which is the common concept for
Java classes and interfaces). The types name is simply the form name, and the target
package is forms.
2. Each Question of a Form is mapped to a JvmField, which is added as member of the
declared type
3. For each Question accessor methods for the eld are generated. The eld gets only a setter
if the value of the Question is not computed by an expression. If the eld is computed,
the content of the getter has to compute the result.
4. For each Question a method is<QUESTIONNAME>Enabled() is inferred. Questions with
computed values are not enabled.
5. For each ConditionalQuestionGroup a method is produced that computes whether the
group is visible or not.
Now place the content into the inferrer class
29
:
1 package org.eclipse.xtext.example.ql.jvmmodel
2
3 import com.google.inject.Inject
4 import java.io.Serializable
5 import org.eclipse.xtext.common.types.JvmOperation
6 import org.eclipse.xtext.common.types.util.TypeReferences
7 import org.eclipse.xtext.example.ql.qlDsl.ConditionalQuestionGroup
8 import org.eclipse.xtext.example.ql.qlDsl.Question
9 import org.eclipse.xtext.example.ql.qlDsl.Questionnaire
10 import org.eclipse.xtext.xbase.XExpression
11 import org.eclipse.xtext.xbase.XbaseFactory
12 import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
13 import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
14 import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
15
16 class QlDslJvmModelInferrer extends AbstractModelInferrer {
17 @Inject extension JvmTypesBuilder
29
https://gist.github.com/kthoms/5132153
28
LWC13 - Submission
18 @Inject TypeReferences typeReferences
19
20 def dispatch void infer(Questionnaire element, IJvmDeclaredTypeAcceptor acceptor, boolean
isPreIndexingPhase) {
21 for (form: element.forms) {
22 acceptor.accept(form.toClass("forms."+form.name))
23 .initializeLater[
24 //implements Serializable
25 it.superTypes +=typeReferences.getTypeForName(typeof(Serializable),element,null)
26
27 members += toField("serialVersionUID",typeReferences.getTypeForName("long",element)
,[final = true ^static = true
28 setInitializer([it.append("1L")])
29 ])
30
31 val allQuestions = form.eAllContents.filter(typeof(Question)).toList
32
33 for (question: allQuestions) {
34 members += question.toField(question.name, question.type)
35 }
36
37 for (question: allQuestions) {
38 if (question.expression == null) {
39 members += question.toGetter(question.name, question.type)
40 members += question.toSetter(question.name, question.type)
41 } else {
42 val getter = question.toGetter(question.name, question.type)
43 getter.body = question.expression
44 members += getter
45 }
46 members += question.createIsEnabledMethod
47 }
48
49 val allQuestionGroups = form.eAllContents.filter(typeof(ConditionalQuestionGroup)).
toList
50 var groupIndex=0;
51 for (questionGroup: allQuestionGroups) {
52 members += questionGroup.createIsGroupVisibleMethod(groupIndex)
53 groupIndex = groupIndex+1
54 }
55
56 ]
57 }
58 }
59
29
LWC13 - Submission
60 def JvmOperation createIsEnabledMethod (Question question) {
61 question.toMethod("is"+question.name.toFirstUpper+"Enabled", typeReferences.
getTypeForName("boolean", question, null)) [
62 body = [it.append(return question.expression == null;)]
63 ]
64 }
65
66 /** Create a method <code>public boolean isGroup[groupIndex]Visible ()</code>.*/
67 def JvmOperation createIsGroupVisibleMethod (ConditionalQuestionGroup group, int
groupIndex) {
68 group.toMethod("isGroup"+groupIndex+"Visible", typeReferences.getTypeForName("boolean",
group, null)) [
69 if(group.condition != null) {
70 body = group.condition
71 } else {
72 body = [it.append(return true;)]
73 }
74 ]
75 }
76
77 }
Now lets take a deeper look at the implementation:
1 class QlDslJvmModelInferrer extends AbstractModelInferrer {
2 @Inject extension JvmTypesBuilder
3 @Inject TypeReferences typeReferences
4 def dispatch void infer(Questionnaire element, IJvmDeclaredTypeAcceptor acceptor, boolean
isPreIndexingPhase) {
5 ...
6 }
7 }
The inferrer class implements IJvmModelInferrer, but for convenience we derive from its
abstract implementation AbstractModelInferrer. The main method to implement is infer().
In the case of QL models, the root element of model resources is a Questionnaire. The base
implementation uses polymorphic dispatching on the root element of a model resource, and
the infer() method of our implementation hooks into the dispatching by using the dispatch
keyword. That is also why the rst argument can be of type Questionnaire, and not of the
base type EObject, like dened in the infer() method that is denied in IJvmModelInferrer.
The implementation uses two services, which are injected as members into the class:
The JvmTypesBuilder oers factory and builder functions to create instances of JVM
30
LWC13 - Submission
Model types. The additional keyword extension has the eect, that the methods of
the JvmTypesBuilder become so-called extension methods. This means, the functions
become implicitly available as additional methods on the rst argument of the function.
We will see extensive use of this nice feature of Xtend in the implementation of the Xtend
based code generator in the next chapter.
TypeReferences is used to retrieve the respective JVM Model instances for given qualied
Java class names through its getTypeForName() methods.
1 for (form: element.forms) {
2 acceptor.accept(form.toClass("forms."+form.name))
3 .initializeLater[
4 ...
5 ]
6 }
Lets take a deeper look on the infer() method. The outer loop simply iterates over the Form
instances of the Questionnaire element. Inside the loop we rst derive a Class instance for each
Questionnaire element in package forms. JVM Model Inference is executed in two phases: In
the rst phase all types are derived, without any content. In the second phase, the content of
the types is derived. This is done by the closure passed to initializeLater(). The reason
why this has to happen this way is that during inference of type members, they could refer
again to types that are derived by the inferrer. The two phases prevent circular calls.
1 it.superTypes +=typeReferences.getTypeForName(typeof(Serializable),element,null)
2
3 members += toField("serialVersionUID",typeReferences.getTypeForName("long",element),
4 [final = true ^static = true
5 setInitializer([it.append("1L")])
6 ])
We want to make the resulting Java class serializable. This is optional, but better style. The-
refore the class has to implement the java.io.Serializable interface, whose JVM Model
representative is retrieved from the TypeReferences instance and added to the superTypes
collection. The identier it denotes the implicit variable of type Form of the closure. It is not
necessary to qualify it here, it could be left out. The closure passed to the setInitializer()
method initializes the eld with the value 1 of type long.
1 val allQuestions = form.eAllContents.filter(typeof(Question)).toList
2
3 for (question: allQuestions) {
4 members += question.toField(question.name, question.type)
5 }
31
LWC13 - Submission
All Question instances from the resource are bound to the nal variable allQuestions. Since
Questions can be nested into groups, the content has to be searched recursively. eAllContents
will traverse over all elements.
Next, for each Question a JvmField instance is inferred. Here the JvmTypesBuilder is helping
us with the method toField, which gets the name and type of the derived eld. Here we see the
eect of the extension keyword: It seems that toField is actually a method of type Question,
but it is a method of the JvmTypesBuilder class.
1 for (question: allQuestions) {
2 if (question.expression == null) {
3 members += question.toGetter(question.name, question.type)
4 members += question.toSetter(question.name, question.type)
5 } else {
6 val getter = question.toGetter(question.name, question.type)
7 getter.body = question.expression
8 members += getter
9 }
10 ...
11 }
The next loop creates the accessor methods for the elds. We could have done this in the
previous loop also, but it is better style to declare the elds rst, and methods next in the
class. The inferred JvmDeclaredType will be translated to Java later, so it is better to have
that clean from the beginning.
Within the loop, we decide if the question has a computation expression or not. If it hasnt one,
it is a simple eld with getter and setter, where we call the toGetter()/toSetter() builder
functions. If the question value is computed by an expression, it does not make sense to oer
a setter method. The eld needs to be read-only. The getter method does not simply return
the value of a eld. Instead, the method has to evaluate the expression. Thus, we assign the
expression as body of the method.
1 for (question: allQuestions) {
2 ...
3 members += question.createIsEnabledMethod
4 }
5
6 ...
7 def JvmOperation createIsEnabledMethod (Question question) {
8 question.toMethod("is"+question.name.toFirstUpper+"Enabled",
32
LWC13 - Submission
9 typeReferences.getTypeForName("boolean", question, null)) [ body = [it.append(return
question.expression == null;)]
10 ]
11 }
For each Question a method boolean is<QUESTIONNAME>Enabled() is inferred. The body of
the method does simply return true if the Question does not have an computation expression
assigned, or false otherwise.
In this case we assign to the body a closure that computes the method implementation text.
This is the rst example where we make use of Xtends Rich String feature (the text between
the three single quotes ), which is later heavily used in the code generator templates.
1 val allQuestionGroups = form.eAllContents.filter(typeof(ConditionalQuestionGroup)).toList
2 var groupIndex=0;
3 for (questionGroup: allQuestionGroups) {
4 members += questionGroup.createIsGroupVisibleMethod(groupIndex)
5 groupIndex = groupIndex+1
6 }
7
8 def JvmOperation createIsGroupVisibleMethod (ConditionalQuestionGroup group, int groupIndex)
{
9 group.toMethod("isGroup"+groupIndex+"Visible", typeReferences.getTypeForName("boolean",
group, null)) [
10 if(group.condition != null) {
11 body = group.condition
12 } else {
13 body = [it.append(return true;)]
14 }
15 ]
16 }
We now lter all ConditionalQuestionGroup instances from the Questionnaire and loop over
them. For each of them, a method is<QUESTIONGROUPINDEX>Visible() is produced. Unfor-
tunately, question groups are anonymous, thus we maintain an index counter and name the
methods isGroup<IDX>Visible().
Since condition expressions for groups are optional, the method body has to return simply
true in the case that no expression is assigned. When groups have a condition, the condition
expression is assigned as the method body.
33
LWC13 - Submission
2.9 Scoping
Scoping is, roughly said, the computation of referrable names in a given context. It is a quite
complex topic, and we wont cover it here into deep. The topic itself is heavily documented by
the Xtext user manual
30
, several articles
31
and implementation examples
32
.
The Xtext framework already provides default implementations to solve scoping and linking.
In the case of Xbase based languages the XbaseBatchScopeProvider is congured by default
as implementation of the IScopeProvider interface.
For our use case, the default behavior is already sucient. Through the JVM model inference
the implicit this variable is already bound to the class representing the form, and thus the
elds representing the question elements are known as callable features.
30
Xtext manual:Scoping
31
e.g. http://blogs.itemis.de/stundzig/archives/776
32
e.g. https://github.com/LorenzoBettini/xtext-scoping
34
LWC13 - Submission
3 Developing the Code Generator
3.1 Reference Implementation
Before implementing a code generator one has to know what the target code is. Therefo-
re a reference implementation has been developed which was coded to large degree manu-
ally. From this reference code the templates can be derived. Also this is a manual step.
We use a Java Server Faces (JSF, see 1.2) based
application, which can be deployed on any Java
Web container (also known as a Servlet contai-
ner) like Glasssh, JBoss and Apache Tomcat.
The screenshot shows the structure of the web
application project. The application is available
for download from the project homepage (JSF-
QL-1.0.zip)
Large parts of the application are not derivable
from the model, they build the skeleton of the
project. This is:
Custom types (src/types/*)
Custom type converter (src/converter/*)
Libraries
(WebContent/WEB-INF/lib/*)
Web Application Descriptor
(WebContent/WEB-INF/web.xml)
Faces conguration
(WebContent/WEB-INF/faces-config.xml)
Images
(WebContent/resources/default/img/*)
Page Templates
(WebContent/resources/default/templates/*)
After describing some necessary conguration
35
LWC13 - Submission
of the IDE we will focus on the parts which are dependent on the QL model and thus subject
of code generation in sub sesction 3.1.4. These artifacts are:
Java Bean classes representing the state of a Form (src/forms)
JSF enabled XHTML pages representing the presentation of a Form (WebContent/forms/*)
3.1.1 IDE Conguration
Webtools To get a nicely integrated developement environment we will install some com-
ponents of the Web Tools Platform (WTP)
33
into an existing Eclipse installation.
install new software
http://download.eclipse.org/releases/juno/
Web, XML, Java EE and OSGi Enterprise Development
Eclipse Java EE Developer Tools 3.4.0.v201107072300
JavaServer Faces Tools (JSF) Project 3.4.1.v201208241503
JST Server Adapters 3.2.200.v20120517_1442
JST Server UI 3.4.0.v20120503_1042
JST Server Adapters Extensions 3.3.101.v20120821_1416
Eclipse Web Developer Tools 3.4.1.v201208170345
Eclipse Java Web Developer Tools 3.4.1.v201208231800
Tomcat installation add new server: - tomcat v7
3.1.2 Import and run reference
Checkout from git OR download from project home page @ code.google.com
Import project from git Checkout instructions:
33
http://www.eclipse.org/webtools/
36
LWC13 - Submission
https://code.google.com/a/eclipselabs.org/p/lwc13-xtext/source/checkout
Download zip from project homepage Download zip from: http://code.google.com/a/
eclipselabs.org/p/lwc13-xtext/
Run as MWE2 Workow
1. /org.eclipse.xtext.example.ql/src/org/eclipse/xtext/example/ql/GenerateQlDsl.mwe2
2. /org.eclipse.xtext.example.qls/src/org/eclipse/xtext/example/qls/GenerateQlsDsl.mwe2
Run conguration
- LWC13 Runtime
import existing project -
lwc13-xtext
examples
QLTest
3.1.3 Main Layout
The logical entry to the web application is the welcome le WebContent/index.xhtml decla-
red in WebContent/WEB-INF/web.xml. The index.xhtml composes the main layout with page
contents and will later be helpful to integrate the generated artifacts with the web applications
layout by using JSFs XHTML templating
34
.
1 <?xml version=1.0 encoding=UTF-8 ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4 <html xmlns="http://www.w3.org/1999/xhtml"
5 xmlns:h="http://java.sun.com/jsf/html"
6 xmlns:ui="http://java.sun.com/jsf/facelets">
7 <body>
8
9 <ui:composition
10 template="/resources/default/templates/defaultLayout.xhtml">
11 <ui:define name="content">
12 Hello World!
13 </ui:define>
14 </ui:composition>
34
http://docs.oracle.com/javaee/6/javaserverfaces/2.1/docs/vdldocs/facelets/
37
LWC13 - Submission
15
16 </body>
17 </html>
Subpages of the application should use the index.xhtml placed in WebContent/ itself as their
template and overwrite the content section with custom output via the same pattern.
1 <ui:composition template="/index.xhtml">
2 <ui:define name="content">
3 ...my xhtml content
4 </ui:define>
5 </ui:composition>
Everything between the opening and closing facelet:define tag within the subpage will be
passed into a corresponding facelet:insert section (name attribute is set to content)
dened in the template (here: defaultLayout.xhtml) or one of its parent templates when the
HTML output is rendered by the JSF framework.
Default template
We keep the main layout denition and the page contents separated from each other. The
WebContent/index.xhtml denes the main composition of the applications structural layout
and content of pages as described in section 3.1.3. Layout template dentions should be placed
in a folder WebContent/resources/*templateName* in our web application.
To change the main layout it is just necessary to change the template reference of the
facelets:composite in WebContent/index.xhtml.
1 ...
2 <ui:composition
3 template="/resources/default/templates/defaultLayout.xhtml">
4 ...
The reference application is shipped with a default template placed in
WebContent/resources/default/. It is a very simple one providing only a skeleton
/templates/defaultLayout.xhtml with basically 3 sections (header, content, footer) where
clients can add custom content.
The current web application expects a dened facelets:insert section with name content
38
LWC13 - Submission
within the template or one of its parents for proper composition. In our reference implementa-
tion it is declared in /resources/default/templates/defaultLayout.xhtml.
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:h="http://java.sun.com/jsf/html"
5 xmlns:ui="http://java.sun.com/jsf/facelets">
6 <h:head>
7 <title><ui:insert name="title">LWC 2013 Xtext</ui:insert></title>
8 </h:head>
9 <body>
10 <div id="header">
11 <ui:insert name="header">
12 <ui:include src="/resources/default/templates/header.xhtml" />
13 </ui:insert>
14 </div>
15 <div id="content">
16 <ui:insert name="content">
17 Content area. Compose by use of tag facelet:define & name="content".
18 </ui:insert>
19 </div>
20 <div id="footer">
21 <ui:insert name="footer">
22 <ui:include src="/resources/default/templates/footer.xhtml" />
23 </ui:insert>
24 </div>
25 </body>
26 </html>
We added two facelets:insert sections to give the possibility to replace the header and
footer. To simplify the concrete JSF compositions later we let JSF include default content for
both by use of facelets:include for a xed source src.
Our structural layout denition which is composed by using
/resources/default/templates/defaultLayout.xhtml as template in
WebContent/resources/default/ has a Cascading Style Sheet (CSS)
35
where ne grained
layouting can be done.
35
http://de.wikipedia.org/wiki/Cascading_Style_Sheets
39
LWC13 - Submission
The applications style sheets consist of 2 les.
container.css - denes styles for main page elements, basically we took the style of
http://www.itemis.de/
base.css - denes extended elements like grids,see http://www.yaml.de/docs/
Because we are not focusing on layout topics, we just did some small things to integrate the
style with the contents like adding CSS classes to XHTML elements. The CSS denitions are
added to show how dividing of content and styles can be done.
3.1.4 Reference Forms
The basic structure of our web application was described in the previous sections. This was the
part of the application which was very general and can be reused for any other application. In
the following we will describe the specic content which enables the questionnaire application
specied in the LWC 2013 task description.
The questionnaire content basically consist of 3 main artifacts:
WebContent/forms/index.xhtml - a link for each form will
be available here, it uses WebContent/index.xhtml as tem-
plate and will be the template for concrete forms in our
application
WebContent/forms/HouseOwning.xhtml - this le will im-
plement the questionnaire form, it uses
WebContent/forms/index.xhtml as its template
src/forms/HouseOwning.java - the so called BackingBe-
40
LWC13 - Submission
an
36
which holds the state of a questionnaire form
Within the reference application there are 3 additional artifacts
which can be seen as some kind of utils:
Money.java - a custom type that can be used in a bean
MoneyConverter.java - a custom converter which will be
used to convert values entered in web pages to custom types used in the bean and vise
versa
TypeFactory.java - a Factory that creates any kind of com-
plex types (e.g. instances of custom type Money)
Form
The form of our reference application consists of dierent areas where each denes a single
element of the questionnaire. Each question has a label and an input element. Whenever the
user changes a value of an input element the page should reload partly by using AJAX
37
.
In the following listings and screenshots you can see how the dierent parts of the questionnaire
are dened and how they are rendered.
The following represents a question which can be answered by yes OR no. The label and
checkbox are grouped by using a so called div to create a close relation between them.
1 <div class="ym-grid">
2
3 <h:outputLabel styleClass="ym-g33 ym-gl"
4 value="Did you sell a house in 2010?" />
5
6 <h:selectBooleanCheckbox styleClass="ym-g50 ym-gl"
7 id="chkHasSoldHouse" value="#{houseOwning.hasSoldHouse}">
8 <f:ajax execute="chkHasSoldHouse"
9 render="grp_hasSoldHouse_hasBoughtHouse" />
10 </h:selectBooleanCheckbox>
11
12 </div>
The JSF html:outputLabel tag is rendered to a HTML label tag on server side before the
server responses on client requests.
36
http://docs.oracle.com/javaee/5/tutorial/doc/bnaqm.html
37
https://en.wikipedia.org/wiki/Ajax_(programming)
41
LWC13 - Submission
1 <label class="ym-g33 ym-gl">
2 Did you sell a house in 2010?
3 </label>
The JSF html:selectBooleanCheckbox will be translated in a more complex HTML:input tag.
The most interesting thing is the action denition onclick which lets JSF do some magic via
AJAX to trigger partial page reloads. The base functionality is provided by the JSF framework
and some JavaScript libraries.
1 <input id="houseOwningForm:chkHasSoldHouse"
2 class="ym-g50 ym-gl" type="checkbox"
3 onclick="mojarra.ab(
4 this,event,valueChange,houseOwningForm:chkHasSoldHouse,houseOwningForm:
grp_hasSoldHouse_hasBoughtHouse
5 )"
6 checked="checked"
7 name="houseOwningForm:chkHasSoldHouse">
8 </input>
On client side the browser renders the question well grouped by use of some CSS framework
classes already mentioned in section 3.1.3.
The following CSS classes out of WebContent/resources/default/css/base.css are respon-
sible for the shown layout.
ym-grid - denes that childs should be grouped in a table
ym-g*number - denes the width of an element
ym-gl - denes that the element should oat to the left of its container
Other question types are following the same pattern of a label and a proper input element to
interact with the application.
1 <div class="ym-grid">
2
3 <h:outputLabel styleClass="ym-g60 ym-gl"
4 value="Price the house was sold for:" />
5
6 <h:inputText styleClass="ym-g33 ym-gl" id="inSellingPrice"
42
LWC13 - Submission
7 value="#{houseOwning.sellingPrice.amount}">
8 <f:ajax event="keyup" execute="inSellingPrice"
9 render="grp_ValueReside" />
10 </h:inputText>
11
12 </div>
As you can see in the gure above the denition of an JSF html:inputText tag is also very
easy. With #{...} it is possible to access a bean and its values. In our sample we access a
property amount of a property sellingPrice of a bean with name houseOwning. :)
1 <input id="houseOwningForm:inSellingPrice"
2 class="ym-g33 ym-gl"
3 type="text"
4 onkeyup="mojarra.ab(
5 this,event,keyup,houseOwningForm:inSellingPrice,houseOwningForm:grp_ValueReside
6 )"
7 value="0"
8 name="houseOwningForm:inSellingPrice">
9 </input>
3.2 Xtend
Since we will use Xtend to write the code generator, this chapter describes some basic concepts
of the Xtend language. However, we will not cover all aspects of Xtend in this section, for more
information see also the ocial documentation
38
.
Xtend is a statically-typed general purpose language similar to Java. Xtend uses Xbase as core
language which was already roughly explained in section 2.6. Hence, the presented concepts of
Xbase are also valid for Xtend. The main focus of Xtend lies in providing a language that is
more readable than Java in certain situations. In the background, Xtend compiles to Java code.
Thus, it plays perfectly together with Java, e.g. methods declared in Java classes can be called
in Xtend and vice versa. Several concepts of Xtend are especially benecial when writing code
generators.
38
http://www.eclipse.org/xtend/documentation.html
43
LWC13 - Submission
1 def someMethod() {
2 var myQuestion = where do we go?
3 myQuestion = myQuestion.toFirstLower
4 val myAnswer = 42
5 The answer for question +myQuestion+ is: +myAnswer
6 }
In Xtend, a method is dened with the def keyword. The return type is optional and will be
automatically inferred. Only when a method is recursively invoking itself, a return type needs
to be specied explicitly. The keyword var denes a variable; constant values are dened with
val. In Xtend - since it is based on Xbase - everything is an expression, meaning that it has
a return type. Consequently, the last expression of a method denes the return value and also
the return type of the method.
In line 3 a so-called extension method is used. Recall that the String class in Java does not
provide a method toFirstUpper. Xtend allows for extending closed types without changing
them (maybe you already guess where Xtend got its name from). You can easily write your
own extensions, e.g. for the Integer type:
1 def square(Integer input) {
2 input * input
3 }
4
5 def useExtension() {
6 16.square // returns 256
7 }
What Xtend basically does is changing the syntax of how methods are called. Instead of writing
toLastUpper(Hello), Xtend always oers the alternative to use the rst input parameter
as receiver of the method call (Hello.toLastUpper). This results in the syntax being more
chained than nested which improves readability. To use methods from another class as extension
methods in your class, the eld dening an object of the other class needs to be marked with
the extension keyword. In our scenario we will use dependency injection like the following:
1 class MyGenerator implements IGenerator{
2 @Inject extension IJvmModelAssociations
3 ...
4 }
This statement simply allows to use the methods declared by the interface
IJvmModelAssociations in our generator class as extension methods on our objects. Which
44
LWC13 - Submission
concrete implementation of the interface is later actually called, is congured in the Guice
module.
A further useful feature of Xtend is polymorphic dispatching. Using the dispatch keyword on
multiple methods with identical signatures has the eect that the decision on which method
should be called is based on the runtime type of the target object. In contrast, Java binds
methods at compile time based on the static type of the target object. Since Xtend compiles
to Java, polymorphic dispatching is internally realized by a dispatcher method using a cascade
of instanceof constructs.
1 def dispatch doSomething(SubTypeA input) {
2 // do something SubTypeA specific
3 }
4
5 def dispatch doSomething(SubTypeB input) {
6 // do something SubTypeB specific
7 }
8
9 def useDispatching() {
10 val SuperType a = new SubTypeA()
11 val SuperType b = new SubTypeB()
12 a.doSomething + b.doSomething
13 }
In this example the types SubTypeA and SubTypeA are sub types of SuperType. The method
doSomething is declared for each of the two sub types as input parameter with accordingly
dierent body (here only indicated by comments). In line 12 these methods are called on the
objects a and b which have SuperType as static type and one of the sub types as compile time
type. Xtend invokes the correct method here, i.e. for a the method in lines 1-3 and for b the
one in lines 5-7 is called.
Xtend oers the possibility to dene Rich Strings (also called templates) which is especially
useful when writing code generators. Rich Strings allow for writing complex Strings with line
breaks and indentations without the need for concatenating special characters like \n or
\t. A Rich String construct starts and ends with triple single quotes () Within such a Rich
String code pieces which themself return a String can be inserted (surrounded by guillemots
). If you wonder where you can nd these guillemot brackets on your keyboard, they are
bound to CTRL+< and CTRL+> in the Xtend editor. There are also logical structures like
for loops or if-else statements supported:
1 def htmlContent(List<String> contents)
2 <html>
45
LWC13 - Submission
3 <body>
4 FOR content: contents BEFORE <p> SEPARATOR <br/> AFTER </p>
5 IF content.length > 10
6 Large Content: content.toFirstUpper
7 ELSE
8 Small Content: content.toFirstUpper
9 ENDIF
10 ENDFOR
11 </body>
12 </html>
Note the special keywords in the FOR loop declaration: BEFORE and AFTER will be called once
before and after the iteration, but only if the loop will be iterated at least once. With the
SEPARATOR keyword a string which will be inserted between two iteratins can be specied.
Calling the example method with [hello, Some more text] results in:
1 <html>
2 <body>
3 <p>
4 Small Content: Hello<br/>
5 Large Content: Some more text
6 </p>
7 </body>
8 </html>
Last but not least, Xtend oers a more sophisticated switch-case statement than Java does.
The break statement as known from Java is implicit in Xtend. Furthermore, switching based
on Strings and even based on the type of the switch argument is possible, like in the following
example:
1 def getTypeName(Number input) {
2 switch (input) {
3 Integer: "It is an Integer!"
4 Float: "It is a Float!"
5 default: "It is some other number type."
6 }
7 }
Now as you know the basic concepts of Xtend, lets nally start writing the code generator.
46
LWC13 - Submission
3.3 Code Generator
In this section you will learn how to implement the code generator for the target application.
For simplicity, the code generator templates are placed in the org.eclipse.xtext.example.ql
project in a sub-package generator. Usually it would be better to create a separate project
which contains the generator, since the language is independent from a single target platform.
It would be possible to create dierent code generators for dierent target platforms, and it
would be better to implement each of them as separate projects.
Generator templates in Xtend are implementations of the IGenerator interface:
1 package org.eclipse.xtext.generator;
2
3 public interface IGenerator {
4 /**
5 * @param input - the input for which to generate resources
6 * @param fsa - file system access to be used to generate files
7 */
8 public void doGenerate(Resource input, IFileSystemAccess fsa);
9 }
3.3.1 Dispatcher template
The code generator is invoked with a Resource instance, which holds a Questionnaire instan-
ce. We have to generate multiple artifacts for each resource, so it is a common pattern to create
a template class which serves as entry point and dispatches to other template classes to create
the artifacts. Usually one template per artifact is created.
Create the class Root.java in package org.eclipse.xtext.example.ql.generator:
1 package org.eclipse.xtext.example.ql.generator;
2
3 import javax.inject.Inject;
4
5 import org.eclipse.emf.ecore.resource.Resource;
6 import org.eclipse.xtext.generator.IFileSystemAccess;
7 import org.eclipse.xtext.generator.IGenerator;
8 import org.eclipse.xtext.xbase.compiler.JvmModelGenerator;
9
10 @SuppressWarnings("restriction")
11 public class Root implements IGenerator {
12 @Inject
47
LWC13 - Submission
13 JvmModelGenerator jvmModelGenerator;
14
15 public void doGenerate(Resource input, IFileSystemAccess fsa) {
16 // dispatch to other generators
17 jvmModelGenerator.doGenerate(input, fsa);
18 }
19 }
As a rst generator to which is dispatched, we inject an instance of JvmModelGenerator.
This is a standard generator shipped with Xtext which translates types inferred by the Jvm
Model Inferrer to Java classes. In our case, the Java class for Forms are generated by the
JvmModelGenerator. In JSF terms, we speek of the Backing Bean.
Next, Xtext has to know that Root is the template that has to be invoked as generator imple-
mentation. Whenever a default implementation must be exchanged by a custom one, this has to
be added or overridden in the respective Guice module. In the case of the custom IGenerator
implementation, this has to be added to the QlDslRuntimeModule class. Open this class and
add a conguration that binds the IGenerator interface to the Root class.
1 @Override
2 public Class<? extends IGenerator> bindIGenerator() {
3 return Root.class;
4 }
Now we are ready to add additional templates and register them in the Root class.
3.3.2 Output Conguration Provider
In JSF applications all the web related content is normally placed under ./WebContent instead
of ./src-gen, which is mostly used as output for generated java artifacts. We want to adapt
to the web applications structure and seperate generated java classes and JSF artifacts from
each other. For that purposes add a class called JsfOutputConfigurationProvider.java deri-
ved from org.eclipse.xtext.generator.OutputConfigurationProvider
39
within package
org.eclipse.xtext.example.ql.generator.The new provider adds an additional
OutputConfiguration for the ouput directory ./WebContent as shown in the listing below.
1 package org.eclipse.xtext.example.ql.generator;
2
3 import java.util.Set;
4
39
http://xtextcasts.org/episodes/15-output-configurations
48
LWC13 - Submission
5 import org.eclipse.xtext.generator.OutputConfiguration;
6 import org.eclipse.xtext.generator.OutputConfigurationProvider;
7
8 public class JSFOutputConfigurationProvider extends OutputConfigurationProvider {
9
10 public final String WEB_CONTENT = "WebContent";
11
12 /**
13 * @return a set of {@link OutputConfiguration} available for the generator
14 */
15 public Set<OutputConfiguration> getOutputConfigurations() {
16 Set<OutputConfiguration> outputConfigurations = super
17 .getOutputConfigurations();
18
19 OutputConfiguration webContent = new OutputConfiguration(WEB_CONTENT);
20 webContent
21 .setDescription("Read-only Output Folder for web generated application artifacts");
22 webContent.setOutputDirectory("./WebContent");
23 webContent.setOverrideExistingResources(true);
24 webContent.setCreateOutputDirectory(true);
25 webContent.setCleanUpDerivedResources(true);
26 webContent.setSetDerivedProperty(true);
27 outputConfigurations.add(webContent);
28
29 return outputConfigurations;
30 }
31 }
The interface OutputConfiguration provides several options to congure the behavior of the
so called Outlet. We use the defaults as in OutputConfigurationProvider except its name,
description and outputDirectoy.
Our JsfOutputConfigurationProvider can be bound in the QlDslRuntimeModule by overri-
ding/extending the method configure.
1 @Override public void configure(Binder binder) {
2 super.configure(binder);
3 binder.bind(IOutputConfigurationProvider.class)
4 .to(JSFOutputConfigurationProvider.class).in(Singleton.class);
5 }
After this step we can refer to the additional OutputConfiguration in generators by use of
the constant WEB_CONTENT dened in class JsfOutputConfigurationProvider.
49
LWC13 - Submission
3.3.3 JSF Generator
After creation of the class Root in section 3.3.1 where we easily can add new Generators and the
dention of the JsfOutputConfigurationProvider in section 3.3.2 which prvides an output
folder for JSF artifacts, we use the New Xtend Class Wizard to create a new Xtend class called
JSFGenerator.xtend in package org.eclipse.xtext.example.ql.generator.
This class will be our entry point to generate JSF related artifacts. The New Xtend Class
Wizard provides the possibility to bind interfaces to the new class by use of the Add button
near the interface section.
As we want to create a new generator we add the interface
org.eclipse.xtext.generator.IGenerator to our new Xtend class. After typing in the
package, the name and the interface of our new Xtend class as shown in the gure above,
we can nish the wizard so that the class shown in the following listing will be created in our
project.
1 package org.eclipse.xtext.example.ql.generator
2
3 import org.eclipse.xtext.generator.IGenerator
4 import org.eclipse.emf.ecore.resource.Resource
5 import org.eclipse.xtext.generator.IFileSystemAccess
6
7 class JSFGenerator implements IGenerator {
8
9 override doGenerate(Resource input, IFileSystemAccess fsa) {
10 throw new UnsupportedOperationException("TODO: auto-generated method stub")
50
LWC13 - Submission
11 }
12 }
To get the created JSFGenarator executed we have to inject and dispatch to it in our dispatcher
template Root.java which was created in section 3.3.1 earlier.
1 package org.eclipse.xtext.example.ql.generator;
2
3 import javax.inject.Inject;
4
5 import org.eclipse.emf.ecore.resource.Resource;
6 import org.eclipse.xtext.generator.IFileSystemAccess;
7 import org.eclipse.xtext.generator.IGenerator;
8 import org.eclipse.xtext.xbase.compiler.JvmModelGenerator;
9
10 public class Root implements IGenerator {
11 @Inject
12 JvmModelGenerator jvmModelGenerator;
13 @Inject
14 JSFGenerator jsfGenerator;
15
16 public void doGenerate(Resource input, IFileSystemAccess fsa) {
17 // dispatch to other generators
18 jvmModelGenerator.doGenerate(input, fsa);
19 jsfGenerator.doGenerate(input, fsa);
20 }
21 }
Now it is time to add some functionality to the JSFGenarator. Open the le JSFGenarator.xtend
and go to the doGenerate extension which is responsible to generate artifacts. Delete the auto-
generated body of the extension - initially it just throws an UnsupportedOperationException
- and add the following lines as rst statements to prevent execution of the generator if the le
extension does not t.
1 if (input.URI.fileExtension!="ql")
2 return
After this pre condition is passed we want to execute the generator logic for our model so it is
a good idea to save the models root node in a variable.
1 val questionnaire = input.contents.head as Questionnaire
Because we want to generate JSF artifacts into the WebContent folder in the following steps
we let Guice add a JSFOutputConfigurationProvider extension to our JSFGenerator.
51
LWC13 - Submission
1 class JSFGenerator implements IGenerator{
2 @Inject extension JSFOutputConfigurationProvider
3 ...
4 }
After this we have the possibility to use the WEB_CONTENT outlet constant as described in section
3.3.2.
The following section 3.3.3 will describe the dierent extensions of the JSFGenerator which
are responsible to generate the JSF artifacts described in 3.1. In a real world project it can
be a good decision to seperate dierent artifacts in dierent Xtend les. Our sample is a very
simple one, so we will add a new extension denition derived from the sample below to the
JSFGenerator.xtend class which encapsulates the logic to generate a single artifact.
1 def generate_Artifact (EObject modelInfo)
2 <?xml version=1.0 encoding=UTF-8 ?>
3 <!-- @generated -->
4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/
xhtml1/DTD/xhtml1-transitional.dtd">
5 <html xmlns="http://www.w3.org/1999/xhtml"
6 xmlns:h="http://java.sun.com/jsf/html"
7 xmlns:ui="http://java.sun.com/jsf/facelets">
8 ... artifact content
9 </html>
10
To get a valid XHTML page we have to generate the DOCTYPE and HTML tag into each
XHTML le. It was replaced in the listings of the following sections to focus on what matters.
JSF Form index
To get simple access to all generated forms in the application we want to generate an index
page where a link is included for each form which is dened in our model. The generated index
page should be saved in a le called index.xhtml within a subfolder generated/forms/ of
the WEB_CONTENT outlet created in section 3.3.2.
1 class JSFGenerator implements IGenerator{
2 @Inject extension JSFOutputConfigurationProvider
3 @Inject extension QlDslExtensions
4
5 override doGenerate(Resource input, IFileSystemAccess fsa) {
52
LWC13 - Submission
6 if (input.URI.fileExtension!="ql")
7 return
8 // model root
9 val questionnaire = input.contents.head as Questionnaire
10 // generate index page with links to generated forms
11 val contentIndex = generate_FormIndex(questionnaire.forms)
12 val fileNameIndex = "generated/forms/index.xhtml"
13 fsa.generateFile(fileNameIndex,WEB_CONTENT, contentIndex)
14 ...
15 }
16 ...
17 }
For the new artifact add a new extension called def generate_FormIndex. It receives a list of
Form elements. In a short loop it generates a html:outputlink node for each element in the
given list of forms. Because doGenerate is called for each QL resource, the generator currently
has the limitation that all QL model elements have to be dened within the same QL resource
to ensure generation of a correct form index page.
1 def generate_FormIndex (List<Form> forms)
2 ...
3 <ui:composition template="/index.xhtml">
4 <ui:define name="content">
5
6 FOR elem: forms SEPARATOR "<br/>"
7 <h:outputLink value="elem.name.jsf">elem.name</h:outputLink>
8 ENDFOR
9
10 </ui:define>
11 </ui:composition>
12 ...
13
By using the attribute template of a composition tag as already described in 3.1.3, the
structure and styles of index.xhtml will be derived in our form index page. Our generated
form index needs a index.xhtml in the applications root folder which itself or one of its parent
templates denes a facelet:insert section with name content like described in section 3.1.3.
To get the possibility to change the template for all generated les in a single le easily later
we will use the generated form index as template for generated form pages in a later step. The
generated form index looks similar to the one described in section 3.1.4.
53
LWC13 - Submission
QlDslExtensions
When implementing a generator, often some basic logic concerning the information extraction
from the model needs to be implemented. We extracted this functionality into an own Xtend
class for modularity and reuse purposes. All of these functions are used in the JSF Generator
and some of them are even called in the QLS generator which will be described in chapter 4.2
later on. The created Xtend class is called QlDslExtension.xtend:
1 class QlDslExtensions {
2 @Inject extension IJvmModelAssociations
3
4 /**
5 * Computes the FormElements which are accessed by the expression of a Question.
6 */
7 def Iterable<FormElement> getDependentElementsWithExpression (Question q) {
8 if (q.expression != null)
9 return emptyList
10 // The JvmField which is inferred from a Question
11 val JvmField field = q.jvmElements.filter(typeof(JvmField)).head
12 // Get all FormElements which have an expression
13 val Iterable<FormElement> allFormElementsWithExpression = q.form.eAllContents
14 .filter(typeof(FormElement))
15 .filter[it.expression!=null]
16 .toSet
17
18 // search the expressions of the form elements which call the JvmField field in a feature
call
19 val result = allFormElementsWithExpression.filter[
20 val exp = it.expression
21 if (exp instanceof XFeatureCall) {
22 // a simple expression e.g. (XFeatureCall)
23 (exp as XFeatureCall).feature.simpleName == field.simpleName
24 } else {
25 // a complex expression e.g. (XFeatureCall1 - XFeatureCall2)
26 val xfeaturecalls = exp.eAllContents.filter(typeof(XFeatureCall))
27 xfeaturecalls.exists[
28 feature.simpleName == field.simpleName
29 ]
30 }
31
32 ]
33 return result
34 }
35
54
LWC13 - Submission
36 /**
37 * Creates an id for the given domain object.
38 */
39 def String getId (EObject o) {
40 switch (o) {
41 ConditionalQuestionGroup: "group"+allConditionalGroups(o).indexOf(o)
42 Question: o.name
43 Form: o.name.toLowerCase
44 }
45 }
46
47 /**
48 * Get all ConditionalGroups underneath the given context.
49 */
50 def private allConditionalGroups (EObject ctx) {
51 ctx.form.eAllContents.filter(typeof(ConditionalQuestionGroup)).toList
52 }
53
54 /**
55 * Get the parent forms name
56 */
57 def getFormName(FormElement elem){
58 elem.form.name.toFirstLower
59 }
60
61 /**
62 * Get the Form container of the given question.
63 */
64 def getForm(EObject question) {
65 EcoreUtil2::getContainerOfType(question, typeof(Form)) as Form
66 }
67
68 /**
69 * Returns the expression assigned to a FormElement, dependent on subtype for FormElement.
70 */
71 def getExpression (FormElement elem) {
72 switch (elem) {
73 Question: elem.expression
74 ConditionalQuestionGroup: elem.condition
75 }
76 }
77 }
55
LWC13 - Submission
Besides several small helper functions which can be basically read by themselves, the method
getDependentElementsWithExpression needs some more explanation. For a given question,
this method return all other form elements - questions or conditional groups - which use the
question in their expression. This method is used to dene which parts of the web page need
to be updated when a particular question is answered by the user. For this, rst the JVM
eld in the backing bean associated with the input question is fetched (line 11). Afterwards all
form elements which contain an expression (line 13-16) are ltered in a way that only the ones
remain whose expression contains the JVM elds name (19-32). The result of this ltering is
the methods return value (line 33).
3.4 Testing the Questionnaire Application
Its time to test the application we have developed so far. If your runtime environment is still
open you need to restart it. Just switch to the runtime instance and press File / Restart. See also
section 2.5 for more information on how to start your runtime evironment for testing. Once the
runtime environement is running you can test the code generator in the QLText project created
in section 2.5. Code generation process is triggered automatically on the y when the QL model
gets modied. The generated artifacts are located in folder WebContent/generated/forms.
Into this folder also an index.xhtml le is placed which you can use as starting point for the
questionnaire application. Right-click on the index le and press Run As / Run On Server. In
the next dialog choose the Tomcat server which you should have congured in section 3.1.1
(if not, then now is the time to do it) and press Next. If your test project is not already in
the Congured area then it needs to be moved there by using the Add > button. Finally press
Finish to start the application in the integrated browser within eclipse. You can also copy the
link and paste it in a web browser of your choice. The result should be similar to the one on
the next screenshot:
56
LWC13 - Submission
Now its show time! You can modify your QL model and save it. The XHTML les will be
regenerated on the y. The server needs some seconds to reload the backing beans. Afterwards
you can press the Refresh button and see the result immediately.
57
LWC13 - Submission
4 Layout and Styling Language (QLS)
4.1 The Language QLS
In this chapter we will describe how the optional task to dene a language for styling and layout
information can be accomplished in our language workbench. The language QLS should allow
for dening the following information:
Grouping of question forms into pages, sections and subsections
Navigation links between pages
Styling of question texts by dening font style, font weight, font family and color
Dening the appearance of a question by specifying the widget type to render
The following shows an example model that we want to be able to dene with QLS:
1 page HouseOwningPage {
2 section house uses Box1HouseOwning {
3 question hasBoughtHouse [font-style: "italic"]
4 question valueResidue [
5 font-weight: "bold"
6 font-color: "#2233FF"
7 font-family: "Arial"
8 ]
9 }
10 section garage uses GarageOwning {
11 question hasBoughtGarage [widget: Radio["Yepp", "Nope"]]
12 }
13 navigation {
14 CarOwningPage
15 }
16 }
17
18 page CarOwningPage uses CarOwning {
19 // ...
20 }
This example model denes two pages. The rst page consists of two sections, one for the
question form Box1HouseOwning which was dened in chapter 2 and one for a further form
called GarageOwning. The form to be rendered inside a page or a section is specied by the
uses keyword. This denition must be unambiguously, e.g. if there is a form included by a page,
58
LWC13 - Submission
it is not allowed to refer to another form in one of its containing sections. This restriction is
ensured by implementing a corresponding validation. How this can be done is covered in section
5.1. In a section (or page) the styling information for the questions of the included form can
be dened as in lines 3-9 and 11. The navigation keyword allows to dene the order in which
the pages are to be displayed by specifying which page should appear next.
Before we can write the corresponding Xtext grammar, we need to create the DSL projects for
QLS as we have done for the Questionnaire language. For this, refer again to chapter 2.2 and
replace all occurences of ql with qls. The project org.eclipse.xtext.example.qls should
then contain the grammar le QlsDsl.xtext. We dene the content of this le as the following:
1 grammar org.eclipse.xtext.example.qls.QlsDsl with org.eclipse.xtext.common.Terminals
2
3 generate qlsDsl "http://www.eclipse.org/xtext/example/qls/QlsDsl"
4 import "http://www.eclipse.org/xtext/example/ql/QlDsl" as ql
5
6 QuestionnaireStyleModel:
7 pages+=Page*;
8
9 Page:
10 "page" name=ID ("uses" form=[ql::Form|ID])? "{"
11 element+=PageElement*
12 navigation=Navigation?
13 "}"
14 ;
15
16 PageElement:
17 QuestionStyling | Section
18 ;
19
20 QuestionStyling:
21 "question" question=[ql::Question] styling+=StyleInformation?
22 ;
23
24 StyleInformation: {StyleInformation}
25 "["(
26 ("font-style:" fontStyle=STRING)? &
27 ("font-weight:" fontWeight=STRING)? &
28 ("font-color:" fontColor=STRING)? &
29 ("font-family:" fontFamily=STRING)? &
30 ("widget:" widget=Widget)?
31 )"]"
32 ;
33
59
LWC13 - Submission
34 Widget: {Widget}
35 widgetType=("Radio"|"DropDown"|"CheckBox"|"Text"|"Slider") ("[" labels+=STRING ("," labels
+=STRING)* "]")?
36 ;
37
38 Section:
39 "section" name=ID ("uses" form=[ql::Form|ID])? "{"
40 element+=PageElement*
41 "}"
42 ;
43
44 Navigation: {Navigation}
45 "navigation" "{" (nextPage+=[Page|ID])+ "}"
46 ;
After reading chapter 2.2 you should be familiar with the concepts of Xtext grammar denitions
and understand most parts of the QLS grammar. One new concept represented in the QLS
grammar is the one of unordered lists:
1 StyleInformation: {StyleInformation}
2 "["(
3 ("font-style:" fontStyle=STRING)? &
4 ("font-weight:" fontWeight=STRING)? &
5 ("font-color:" fontColor=STRING)? &
6 ("font-family:" fontFamily=STRING)? &
7 ("widget:" widget=Widget)?
8 )"]"
9 ;
The styling information can be dened in an arbitrary order in which each kind of element
(font style, font color and so on) may only occur at most once. The arbitrariness of the order
is expressed by the & between the style elements. The optionality of each style information
is again declared by the question mark ?.
A further noteworthy aspect is the handling of references. In Xtext, references to language
elements are expressed by using squared brackets. An example for this are the references to
existing pages in the navigation section:
1 Navigation: {Navigation}
2 "navigation" "{" (nextPage+=[Page|ID])+ "}"
3 ;
60
LWC13 - Submission
Here, nextPage is a reference to an existing page description which may even be dened in a
dierent le. ID denes which attribute should be used for the references name. Xtext auto-
matically creates hyperlinks to the referenced elements which can be enabled by holding Ctrl
and moving the curser over the reference or by pressing F3.
To dene references to elements dened in other langauges than the own, the reference needs to
be qualied. An example in QLS is the reference to questions or forms dened in a QL model.
To express this on the grammar level, the QL DSL needs rst to be imported:
1 import "http://www.eclipse.org/xtext/example/ql/QlDsl" as ql
Since the QL DSL is imported under the name ql, references can now be qualied by using a
double colon (::):
1 QuestionStyling:
2 "question" question=[ql::Question] styling+=StyleInformation?
3 ;
This grammar rule allows for refering all question elements dened in any QL model in our test
project. However, since there is always an unambiguous form specied for a page or a section,
only the questions dened within this form should be referable. This is a typical scoping issue
which needs to be handled in the scope provider of the QLS language. We already learned about
scoping in section 2.9. The scope provider for the QLS language looks like the following:
1 class QlsDslScopeProvider extends AbstractDeclarativeScopeProvider {
2
3 @Inject extension QlsDslExtensions
4
5 def IScope scope_QuestionStyling_question(EObject context, EReference ref) {
6 Scopes::scopeFor(
7 EcoreUtil2::getAllContentsOfType(context.form, typeof(Question))
8 );
9 }
10 }
The method scope_QuestionStyling_question is always invoked whenever the visible ele-
ments for the attribute question in the grammar rule QuestionStyling need to be computed.
This is for example the case when the content assist needs to compute the elements to be pro-
posed. The methods implementation is quite simple. The static method scopeFor expects a
list of elements which are in scope (=visible) in the current context. For computing the visible
elements, we use a recursive algorithm to get the form declaration of the parent section or page
which is mandatory. This algorithm is dened in the Xtend class QlsDslExtensions which is
61
LWC13 - Submission
imported in line 3. Note, that the expression context.form in line 7 will call the correspon-
ding extension method in QlsDslExtensions. There, we use Xtends polymorphic dispatching
feature (see also section 3.2):
1 class QlsDslExtensions {
2
3 def dispatch Form getForm(Section section) {
4 if (section.form != null) {
5 section.form
6 }
7 else {
8 section.eContainer.form
9 }
10 }
11
12 def dispatch getForm(Page page) {
13 page.form
14 }
15 }
Note, that for the rst method the return type Form needs to be declared explicitly since
it is recursively calling itself in line 8 and thus its return type cannot be derived automa-
tically. The calculated form is used in the scope provider as input for the helper method
getAllContentsOfType which is used to collect all questions dened in the given form.
Now its time to play around with the QLS language and to test its features. For this, switch
to the test project in the runtime environment (see also section 2.5) and create a le with the
le extension .qls.
4.2 QLS Code Generator
So far, our QLS models have no eect on the generated output. The rst step is to think about
which artifacts are to be generated from a QLS model. The QLS model contains two kinds of
information. The rst is layout information: A page consists of one or more forms. Recall that
for each form two XHTML les are already generated, one for the base content of the form
site and a wrapper referencing this base XHTML. Hence a page maps to an XHTML le which
is composed of all specied forms. The second information in QLS is styling information. As
usual in web development, styling is expressed in the CSS format in our tool. To sum up the
actions, for each page:
Generate one CSS le
62
LWC13 - Submission
Generate one XHTML le wiring all form XHTMLs together and reference CSS le
Lets take the following QLS model as a reference example:
1 page HouseOwningPage {
2 section house uses HouseOwning {
3 question hasBoughtHouse [font-style:"italic"]
4 question valueResidue [
5 font-weight: "bold"
6 font-color: "#2233FF"
7 font-family: "Verdana"
8 ]
9 }
10 section garage uses GarageOwning {
11 question hasBoughtGarage [widget: Radio["Yepp", "Nope"]]
12 }
13 navigation {
14 CarOwningPage
15 }
16 }
17
18 page CarOwningPage uses CarOwning {
19 question hasSoldCar [font-color: "green"]
20 question hasBoughtCar [font-color: "red"]
21 }
The intended generated le structure is visualized
in the screenshot on the left. The les in the fol-
der forms are generated by the QL model to code
generator as described in chapter 3. The code ge-
nerator for the QLS model needs to generate the
XHTML les under the folder pages and the CSS
les in resources/default/css/generated. The le
pages/index.xhtml serves as root page containing the
link to the rst actual page to be presented.
The page HouseOwningPage contains two sections
using the forms HouseOwning and GarageOwning, thus
the generated le pages/HouseOwningPage.xhtml wi-
res the two base les forms/HouseOwningBase.xhtml
and forms/GarageOwningBase.xhtml together:
63
LWC13 - Submission
1 <html xmlns="http://www.w3.org/1999/xhtml"
2 xmlns:ui="http://java.sun.com/jsf/facelets"
3 xmlns:h="http://java.sun.com/jsf/html"
4 xmlns:f="http://java.sun.com/jsf/core">
5 <h:head></h:head>
6 <ui:composition template="/index.xhtml">
7 <ui:define name="content">
8 <h:outputStylesheet library="default/css/generated" name="HouseOwningPage.css" />
9 <div><ui:include src="/generated/forms/HouseOwningBase.xhtml" /></div><p/>
10 <div><ui:include src="/generated/forms/GarageOwningBase.xhtml" /></div>
11 <div>
12 <h:outputLink value="CarOwningPage.jsf">CarOwningPage</h:outputLink>
13 </div>
14 </ui:define>
15 </ui:composition>
16 </html>
The two pages are referenced in lines 9 and 10. Line 12 denes the link to the next page as
dened in the navigation section of the QLS model. Line 8 species which CSS le is to be used
to render the sites elements. The CSS le for the house owning page contains all corresponding
styling information which are linked to the corresponding label elements by their ids:
1 #houseowning\:lblHasBoughtHouse {
2 font-style: italic;
3 }
4 #houseowning\:lblValueResidue {
5 color: #2233FF;
6 font-family: Verdana;
7 font-weight: bold;
8 }
9 #garageowning\:lblHasBoughtGarage {
10 }
When JSF converts from XHTML each element gets a unique (full qualied) id. In our scenario
this is always the id of the parent form concatenated with the id of the element itself. As
separator JSF uses a colon. However, colons are special characters in CSS, hence they need to
be escaped.
Now that the intended artifacts to be generated are claried, the code generator itself can be
written. Here again we use Xtend (see section 3.2):
64
LWC13 - Submission
1 class QlsDslGenerator implements IGenerator {
2 @Inject extension JsfOutputConfigurationProvider
3 @Inject extension QlDslExtensions
4
5 override void doGenerate(Resource input, IFileSystemAccess fsa) {
6 if (input.URI.fileExtension!="qls")
7 return
8
9 val styleModel = input.contents.head as QuestionnaireStyleModel
10 for (page: styleModel.pages) {
11 val cssContent = generateCssFile(page);
12 val cssFileName = "resources/default/css/generated/"+page.name+".css"
13 fsa.generateFile(cssFileName, WEB_CONTENT, cssContent)
14
15 val xhtmlContent = generateXhtmlFile(page);
16 val xhtmlFileName = "generated/pages/"+page.name+".xhtml"
17 fsa.generateFile(xhtmlFileName, WEB_CONTENT, xhtmlContent)
18 }
19 val contentIndex = generateIndexPage(styleModel.pages.get(0))
20 fsa.generateFile("generated/pages/index.xhtml",WEB_CONTENT, contentIndex)
21 }
22
23 def generateCssFile(Page page)
24 <!-- @generated -->
25 FOR styleInfo: page.eAllContents.filter(typeof(StyleInformation)).toList
26 styleInfo.id {
27 IF styleInfo.fontColor != nullcolor: styleInfo.fontColor;ENDIF
28 IF styleInfo.fontFamily != nullfont-family: styleInfo.fontFamily;ENDIF
29 IF styleInfo.fontStyle != nullfont-style: styleInfo.fontStyle;ENDIF
30 IF styleInfo.fontWeight != nullfont-weight: styleInfo.fontWeight;ENDIF
31 }
32 ENDFOR
33
34
35 def generateXhtmlFile(Page page)
36 <!-- @generated -->
37 <html xmlns="http://www.w3.org/1999/xhtml"
38 xmlns:ui="http://java.sun.com/jsf/facelets"
39 xmlns:h="http://java.sun.com/jsf/html"
40 xmlns:f="http://java.sun.com/jsf/core">
41 <h:head></h:head>
42 <ui:composition template="/index.xhtml">
43 <ui:define name="content">
44 <h:outputStylesheet library="default/css/generated" name="page.name.css" />
65
LWC13 - Submission
45 IF page.form != null
46 <div class="highlight_section"><ui:include src="/generated/forms/page.form.name
Base.xhtml" /></div>
47 ELSE
48 FOR section: page.eAllContents.toList.filter(typeof(Section)).toList SEPARATOR <p
/>
49 <div class="highlight_section"><ui:include src="/generated/forms/section.form.name
Base.xhtml" /></div>
50 ENDFOR
51 ENDIF
52 IF page.navigation != null
53 <form>
54 <div class="highlight_section ym-grid">
55 <h:outputLabel styleClass="lvl1Lbl ym-gl" id="lblNavigation"
56 value="Next pages:"/>
57 FOR nextPage: page.navigation.nextPage
58 <h:outputLink styleClass="lvl1Lbl ym-gl"
59 value="nextPage.name.jsf">
60 nextPage.name
61 </h:outputLink>
62 ENDFOR
63 </div>
64 </form>
65 ENDIF
66 </ui:define>
67 </ui:composition>
68 </html>
69
70
71 def generateIndexPage(Page page)
72 <?xml version=1.0 encoding=UTF-8 ?>
73 <!-- @generated -->
74 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/
xhtml1/DTD/xhtml1-transitional.dtd">
75 <html xmlns="http://www.w3.org/1999/xhtml"
76 xmlns:h="http://java.sun.com/jsf/html"
77 xmlns:ui="http://java.sun.com/jsf/facelets">
78 <ui:composition template="/index.xhtml">
79 <ui:define name="content">
80 <h:outputLink value="page.name.jsf">page.name</h:outputLink>
81 </ui:define>
82 </ui:composition>
83 </html>
84
85
66
LWC13 - Submission
86 def getId(StyleInformation styleInfo) {
87 val question = (styleInfo.eContainer as QuestionStyling).question
88 "#"+question.form.id+ "\\:lbl"+question.id.toFirstUpper
89 }
90 }
The doGenerate method is similar to the one of the JSFGenerator from section 3.3.3. It basi-
cally denes le names and delegates to other methods for dening the generated les contents
(generateCssFile, generateXhtmlFile, generateIndexPage). We wont go into details here.
Instead lets test the generated application.
For this, you can basically follow the instructions given in section 3.4. To see some genrated pa-
ges you rst need to dene a QLS model. To start the application you can use the index.xhtml
le in folder WebContent/generated/pages. Right-click on this le and press Run As / Run
On Server / Finish. Using the example models in this chapter a web application similar to the
one on the following screenshot will be generated. You can change the style model and refresh
the browser page to see the result immediately.
67
LWC13 - Submission
68
LWC13 - Submission
5 Additional Concepts
5.1 Validation
As an optional task the LWC13 task requires the implementation of analysis rules. Xtext
provides a validation framework which integrates into the EMF Validation framework
40
. The
Xtext User Manual contains a Validation chapter that is worth reading additionally
41
.
We will show in this section how the constraints dened in the task description can be realized
with Xtext.
5.1.1 Extending the Java Validator class
With the rst translation of the Xtext grammar, the generator has already created the necessa-
ry infrastructure to implement custom validation rules. Look into the package
org.eclipse.xtext.example.ql.validation, you will nd a Java class QlDslJavaValidator.
The class extends the AbstractQlDslJavaValidator class, which is regenerated each time the
grammar is translated. Thus, the generation gap pattern is applied here again. It is safe to
extend the QlDslJavaValidator class manually.
But instead of implementing the constraints in Java, we will use Xtend again. For easier inte-
gration, our Xtend based validator class will be inserted into the class hierarchy of
QlDslJavaValidator.
Create an Xtend class QlDslXtendValidator, and extend it from AbstractQlDslJavaValidator.
1 package org.eclipse.xtext.example.ql.validation
2
3 import javax.inject.Inject
4 import org.eclipse.xtext.validation.Check
5 import org.eclipse.xtext.xbase.XFeatureCall
6 import org.eclipse.xtext.xbase.XbasePackage
7 import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations
8
9 import static extension org.eclipse.xtext.nodemodel.util.NodeModelUtils.*
10 class QlDslXtendValidator extends AbstractQlDslJavaValidator {
11 @Inject extension IJvmModelAssociations
12 }
40
http://www.eclipse.org/modeling/emf/?project=validation
41
see http://www.eclipse.org/Xtext/documentation.html#validation
69
LWC13 - Submission
Derive QlDslJavaValidator from the the Xtend validator:
1 public class QlDslJavaValidator extends QlDslXtendValidator {
2 // do nothing here, rules are implemented in Xtend
3 }
5.1.2 Constraint: Ensure order of questions
The rst constraint in the LWC13 task is dened so:
Test for cyclic dependencies. For instance, the fol lowing snippet should be rejected:
1 if (x) { y: "Y?" boolean }
2 if (y) { x: "X?" boolean }
The reason is that y will only be asked for when x is true, but x wil l only get a value
when y is true. Of course such cyclic dependencies could occur transitively and nested
in expressions. Another way of stating this check is: the ordering of questions should
be consistent with how the question variables are used in conditions and computed
values.
The task allows two approaches to achieve the goal. We will choose the second approach: Check
that elements referred in expressions have been declared before their usage.
Xtext does not enforce that elements are declared before they are used somewhere. The referred
names simply must be in the scope of the context, which the current scope implementation
already accomplishes. We could implement this constraint also in the scope provider of the
language by restricting the scope to elements that have been declared before. However, we will
implement this as a semantic constraint in the validator class.
To implement the constraint we need to know the location in the model where a Question
element is declared and where it is called in an expression. Besides the Abstract Syntax Tree
Xtext maintains a second model, which represents the document. This is the so-called Node
Model, and the class NodeModelUtils provides some utility functions to navigate from an AST
element to the node model. Since Questions that are referred in expressions are local to the
Form, we can simply compare the oset in the document of the declared Question and the
feature call in an expression.
Xtext validation rules are implemented as methods which are annotated with @Check. The
method name does not matter. Check methods are expected to have exactly one parameter,
70
LWC13 - Submission
which is of the type of element that has to be checked. The base class provides methods to
create error messages. For the case of this constraint, the necessary context object type is
XFeatureCall.
Now open the QlDslXtendValidator class again and add this method
42
:
1 @Check
2 def void check_featureDeclaredBeforeCall (XFeatureCall featureCall) {
3 val featureSource = featureCall.feature.sourceElements.head
4 val nodeFeature = if (featureSource != null) featureSource.node else featureCall.feature
.node
5 val nodeCall = featureCall.node
6 if (nodeFeature != null) {
7 if (nodeFeature.offset > nodeCall.offset) {
8 error(featureCall.feature.simpleName+" must be declared before.",featureCall,
9 XbasePackage::eINSTANCE.XAbstractFeatureCall_Feature, "
ERR_FEATURE_CALL_BEFORE_DECLARATION", null)
10 }
11 }
12 }
After restarting the workbench the situation will be recognized as an error:
5.1.3 Constraint: Type conformance check
Next, the LWC13 task requires checking the type conformance in expressions:
Type check conditions and variables: the expressions in conditions should be type
correct and should ultimately be booleans. The assigned variables should be assigned
consistently: each assignment should use the same type.
Here we have to do nothing, since this constraint is already implemented in Xbase. This works
thanks to Xbases type inference mechanism. The Xtend language makes heavy use of this nice
feature, which makes it almost unneccessary to declare types anywhere. For dynamic languages
42
https://gist.github.com/kthoms/5240455
71
LWC13 - Submission
this is natural, since the actual type is known and evaluated at runtime. Static typed language
often lack this feature.
5.1.4 Testing validation rules
It is easy to test validation rules in a runtime environment. However, we will show how these
rules can also be unit tested. Also herefore the Xtext framework already contains the necessary
infrastructure. Remember that Xtext has already created a test plugin with the initial generator
run? Now it is time to make use of it.
Again, it is easier to create the test with Xtend. Xtend allows us to create simple models inline
with Rich Strings, pass the result to a parser, and validate the result. With Xtend this is a
one-liner.
1 package org.eclipse.xtext.example.ql.validation.test
2
3 import javax.inject.Inject
4 import org.eclipse.xtext.example.ql.QlDslInjectorProvider
5 import org.eclipse.xtext.example.ql.qlDsl.Questionnaire
6 import org.eclipse.xtext.junit4.InjectWith
7 import org.eclipse.xtext.junit4.XtextRunner
8 import org.eclipse.xtext.junit4.util.ParseHelper
9 import org.eclipse.xtext.junit4.validation.ValidationTestHelper
10 import org.eclipse.xtext.xbase.XbasePackage
11 import org.junit.Before
12 import org.junit.Test
13 import org.junit.runner.RunWith
14
15 @RunWith(typeof(XtextRunner))
16 @InjectWith(typeof(QlDslInjectorProvider))
17 class QlDslValidationTest {
18 @Inject extension ParseHelper<Questionnaire> parseHelper
19 @Inject extension ValidationTestHelper
20
21 @Before
22 def void setUp () {
23 parseHelper.fileExtension="ql"
24 }
72
LWC13 - Submission
25
26 @Test
27 def void testValidation_CallBeforeDeclaration_expectError () {
28
29 form Foo {
30 if (x) { y: "Y?" boolean }
31 if (y) { x: "X?" boolean }
32 }
33 .parse.assertError(XbasePackage::eINSTANCE.XFeatureCall, "
ERR_FEATURE_CALL_BEFORE_DECLARATION", "must be declared before")
34 }
35
36 @Test
37 def void testValidation_CallBeforeDeclaration_expectSuccess () {
38
39 form Foo {
40 x: "foo" boolean
41 if (x) { a: "X?" boolean }
42 }
43 .parse.assertNoErrors
44 }
45
46
47 // Type check conditions and variables: the expressions in conditions should be type
correct and should ultimately be booleans.
48 // The assigned variables should be assigned consistently: each assignment should use the
same type.
49 @Test
50 def void testValidation_ConditionTypeCheck_expectError () {
51
52 form Foo {
53 if ("foo".length) { a: "X?" boolean }
54 }
55 .parse.assertError(XbasePackage::eINSTANCE.XMemberFeatureCall,
56 "org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types", "Type mismatch")
57 }
58
59 @Test
60 def void testValidation_ConditionTypeCheck_expectSuccess () {
61
62 form Foo {
63 if ("foo".length>1) { a: "X?" boolean }
64 }
65 .parse.assertNoErrors
66 }
73
LWC13 - Submission
67
68 @Test
69 def void testValidation_AssignmentTypeCheck_expectFailure () {
70
71 form Foo {
72 a: "X?" boolean ("foo".length)
73 }
74 .parse.assertError(XbasePackage::eINSTANCE.XMemberFeatureCall,
75 "org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types", "Type mismatch")
76 }
77
78 @Test
79 def void testValidation_AssignmentTypeCheck_expectSuccess () {
80
81 form Foo {
82 a: "X?" boolean ("foo".length>1)
83 }
84 .parse.assertNoErrors
85 }
86 }
Basically, the class is a plain JUnit 4 class. We have to use a special JUnit execution class,
XtextRunner, and provide a language specic initializer class, QlDslInjectorProvider.
Next, the class adds two extension classes. ParseHelper provides a parse() method for char se-
quences. This will parse and validate the model. Afterwards the observed issues can be asserted
with the methods from the
1 @RunWith(typeof(XtextRunner))
2 @InjectWith(typeof(QlDslInjectorProvider))
3 class QlDslValidationTest {
4 @Inject extension ParseHelper<Questionnaire> parseHelper
5 @Inject extension ValidationTestHelper
6 ...
7 }
Now the test methods can be implemented. They are super simple:
1 @Test
2 def void testValidation_CallBeforeDeclaration_expectSuccess () {
3
4 form Foo {
5 x: "foo" boolean
6 if (x) { a: "X?" boolean }
7 }
74
LWC13 - Submission
8 .parse.assertNoErrors
9 }
Due to the tight Java integration, the unit tests of the Xtend class can be executed by running
them through the context menu (Run As / JUnit Test).
5.2 Build
The build of software products is usually done on build servers without manual interaction.
In the Eclipse ecosystem several build systems are available. The one gaining most attention
nowadays is Maven Tycho, which is a set of plugins for the well-known Maven build framework.
In Maven, the build descriptors are so-called POM les (usually pom.xml). For the LWC example
we have added such POM les to enable a headless build.
5.2.1 settings.xml
Maven needs to know about repositories from where it can download artifacts. By default,
Maven only knows about Maven Central, which is the repository hosted by Apache. For our
build we need to consume some artifacts that are not available at Maven Central:
The Fornax Workow plugin is used to execute MWE workows to generate code from
the Xtext grammar.
The Xtend plugin compiles Xtend classes to Java code.
All plugin dependencies must be resolvable through Eclipse p2 repositories. The layout p2 is a
special layout contributed by Tycho. The plugins consume dependencies from
75
LWC13 - Submission
Eclipse Juno: Composite repository of the Eclipse Juno simultaneous release. Most plugins
are resolved through this repository.
Eclipse Orbit: Orbit contains OSGi bundles of 3rd party components (like logj, javax.faces,
javax.inject, Google Guava etc.).
Eclipse Xtext: The release repository for Xtext, Xtend, MWE2.
In settings.xml, repository congurations must be contained in a prole. We dene a prole
external-repositories, which is activated by default in the activeProfiles section.
1 <settings>
2 <activeProfiles>
3 <activeProfile>external-repositories</activeProfile>
4 </activeProfiles>
5 <profiles>
6 <profile>
7 <id>external-repositories</id>
8 <repositories>
9 <repository>
10 <id>Eclipse Juno</id>
11 <layout>p2</layout>
12 <url>http://download.eclipse.org/releases/juno</url>
13 </repository>
14 <repository>
15 <id>Eclipse Orbit</id>
16 <layout>p2</layout>
17 <url>http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/
repository/
18 </url>
19 </repository>
20 <repository>
21 <id>Eclipse Xtext</id>
22 <layout>p2</layout>
23 <url>http://download.eclipse.org/modeling/tmf/xtext/updates/composite/releases/
24 </url>
25 </repository>
26 </repositories>
27 <pluginRepositories>
28 <pluginRepository>
29 <id>fornax.releases</id>
30 <name>Fornax Release Repository</name>
31 <url>http://www.fornax-platform.org/nexus/content/repositories/releases/
32 </url>
33 </pluginRepository>
76
LWC13 - Submission
34 <pluginRepository>
35 <id>xtend</id>
36 <url>http://build.eclipse.org/common/xtend/maven/</url>
37 </pluginRepository>
38 </pluginRepositories>
39 </profile>
40 </profiles>
41 </settings>
The settings le is placed in the repository under /devenv/lwc13.devenv/settings.xml.
In order to use this settings le, it has to be put to <HOME>/.m2 or passed with the -s
option to the mvn command.
5.2.2 Parent POM
Since Maven supports a simple POM inheritance, it is common to extract common settings for
a set of project modules into a so-called Parent POM. This pom.xml l is placed in the root of
the repository
43
.
The Parent POM is responsible for dierent things:
Denition of the common group identier and version
1 <groupId>org.eclipse.xtext.example.ql</groupId>
2 <version>1.0.0-SNAPSHOT</version>
Tycho Plugin conguration. Note the <extensions>true</extensions> entry. To make
the Tycho version that is used easier congurable, a property tycho-version is dened,
which is used for all Tycho plugin congurations.
1 <properties>
2 <tycho-version>0.17.0</tycho-version>
3 </properties>
4 <build>
5 <plugins>
6 <plugin>
7 <groupId>org.eclipse.tycho</groupId>
8 <artifactId>tycho-maven-plugin</artifactId>
9 <version>${tycho-version}</version>
10 <extensions>true</extensions>
43
http://code.google.com/a/eclipselabs.org/p/lwc13-xtext/source/browse/pom.xml
77
LWC13 - Submission
11 </plugin>
12 ...
Supported Target Environments. Eclipse is partially OS dependent. Which platforms
should be considered in the target platform conguration is congured with the target-
platform-conguration plugin.
1 <plugin>
2 <groupId>org.eclipse.tycho</groupId>
3 <artifactId>target-platform-configuration</artifactId>
4 <version>${tycho-version}</version>
5 <configuration>
6 <resolver>p2</resolver>
7 <pomDependencies>consider</pomDependencies>
8 <environments>
9 <environment>
10 <os>win32</os>
11 <ws>win32</ws>
12 <arch>x86</arch>
13 </environment>
14 <environment>
15 <os>win32</os>
16 <ws>win32</ws>
17 <arch>x86_64</arch>
18 </environment>
19 <environment>
20 <os>macosx</os>
21 <ws>cocoa</ws>
22 <arch>x86_64</arch>
23 </environment>
24 </environments>
25 </configuration>
26 </plugin>
Java Source Code version. The produced code requires Java 1.6 for compilation. This
needs to be congured at the tycho-compiler-plugin.
1 <plugin>
2 <groupId>org.eclipse.tycho</groupId>
3 <artifactId>tycho-compiler-plugin</artifactId>
4 <version>${tycho-version}</version>
5 <configuration>
6 <encoding>UTF-8</encoding>
7 <meminitial>128m</meminitial>
8 <maxmem>1024m</maxmem>
78
LWC13 - Submission
9 <source>6.0</source>
10 <target>6.0</target>
11 <verbose>true</verbose>
12 </configuration>
13 </plugin>
Maven Plugin Management. In the pluginManagement section the plugins that potentially
participate in the build are congured with their versions. This is because Maven would
select the latest available version of a plugin instead and prints warnings during the build.
It is better to x the versions of plugins that are used to those with which the build has
been tested. Also basic plugin congurations can be added here.
1 <plugin>
2 <groupId>org.eclipse.tycho</groupId>
3 <artifactId>tycho-p2-repository-plugin</artifactId>
4 <version>${tycho-version}</version>
5 </plugin>
6 <plugin>
7 <groupId>org.apache.maven.plugins</groupId>
8 <artifactId>maven-clean-plugin</artifactId>
9 <version>2.4.1</version>
10 </plugin>
11 ...
5.2.3 Reactor POM
The project consists of several modules, which is called a Multi Module Project in Maven terms.
The modules of a project are listed in a <modules> section. Often the modules are enlisted in
the Parent POM, but we splitted this. There is a POM that only contains module entries in
the projects folder
44
:
1 <modules>
2 <module>org.eclipse.xtext.example.ql</module>
3 <module>org.eclipse.xtext.example.ql.ui</module>
4 <module>org.eclipse.xtext.example.qls</module>
5 <module>org.eclipse.xtext.example.qls.ui</module>
6 <module>org.eclipse.xtext.example.ql.sdk</module>
7 <module>org.eclipse.xtext.example.ql.repository</module>
8 </modules>
44
http://code.google.com/a/eclipselabs.org/p/lwc13-xtext/source/browse/projects/pom.xml
79
LWC13 - Submission
When building the DSL projects, this POM has to be executed. Assuming the build is executed
from the repository root, the typical build command would be:
1 mvn -s devenv/lwc13.devenv/settings.xml -f projects/pom.xml clean install
5.2.4 QL Runtime Project POM
Lets look a bit at the POM of the QL Runtime Project org.eclipse.xtext.example.ql.
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/
maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.eclipse.xtext.example.ql</groupId>
7 <artifactId>parent</artifactId>
8 <version>1.0.0-SNAPSHOT</version>
9 <relativePath>../../pom.xml</relativePath>
10 </parent>
11 <artifactId>org.eclipse.xtext.example.ql</artifactId>
12 ...
At the beginning we have the typical Maven coordinates and parent dependency. The Parent
POM is at the root of the repository, which is 2 directories up. The POM one directory up is
the Reactor POM. Note that it is important to mention here the exact same version as declared
in the Parent POM. This module inherits the parents version and groupId coordinates, only
the artifactId changes.
Maven Tycho enforces that the POM version matches Bundle-Version, and the artifactId
must be equal to the Bundle-SymbolicName of the manifest. A bit special are SNAPSHOT
versions, which is a special Maven concept for intermediate development versions of artifacts.
When the project version is a snapshot version, the bundle version must have an additional
qualier. In our case here, the project version is 1.0.0-SNAPSHOT, which means the bundle
version must be 1.0.0.qualifier. The qualier gets replaced at build time by a timestamp.
1 Bundle-Name: org.eclipse.xtext.example.ql
2 Bundle-Version: 1.0.0.qualifier
The next entry, packaging, is especially important.
1 <packaging>eclipse-plugin</packaging>
80
LWC13 - Submission
Tycho introduces some Eclipse specic packaging types, which trigger the module to be built
by Tycho plugins.
1 <build>
2 <resources>
3 <resource>
4 <directory>${project.build.directory}/xtext</directory>
5 </resource>
6 </resources>
7 <plugins>
8 <!-- Copy all Xtext related sources to seperate folder that is registered as resource
folder -->
9 <plugin>
10 <artifactId>maven-resources-plugin</artifactId>
11 <executions>
12 <execution>
13 <id>copy-resources</id>
14 <phase>initialize</phase>
15 <goals>
16 <goal>copy-resources</goal>
17 </goals>
18 <configuration>
19 <outputDirectory>${project.build.directory}/xtext</outputDirectory>
20 <resources>
21 <resource>
22 <directory>src</directory>
23 <includes>
24 <include>**/*.xtext</include>
25 <include>**/*.mwe2</include>
26 </includes>
27 </resource>
28 </resources>
29 </configuration>
30 </execution>
31 </executions>
32 </plugin>
Next, we do something special for MWE2. In order to execute an MWE2 workow this workow
le must be loadable from the projects classpath. But the MWE2 le is contained in the source
path of a project. Therefore an additionaly directory target/xtext is added as resource folder
to the project (resource folders are added to the classpath), and copy Xtext related resources
from the source directory to this directory by use of the maven-resource-plugin.
1 <plugin>
2 <artifactId>maven-clean-plugin</artifactId>
81
LWC13 - Submission
3 <configuration>
4 <filesets>
5 <fileset>
6 <directory>src-gen</directory>
7 <excludes>
8 <exclude>.gitignore</exclude>
9 </excludes>
10 </fileset>
11 <fileset>
12 <directory>xtend-gen</directory>
13 <excludes>
14 <exclude>.gitignore</exclude>
15 </excludes>
16 </fileset>
17 <fileset>
18 <directory>../${project.artifactId}.ui/src-gen</directory>
19 <excludes>
20 <exclude>.gitignore</exclude>
21 </excludes>
22 </fileset>
23 <fileset>
24 <directory>../../tests/${project.artifactId}.tests/src-gen</directory>
25 <excludes>
26 <exclude>.gitignore</exclude>
27 </excludes>
28 </fileset>
29 </filesets>
30 </configuration>
31 </plugin>
When the workow for an Xtext project is executed, Xtext generates code not only into the
runtime project, but also into the UI and Tests project. When these projects are build in a
multi-module build, each project is built one after the other, and all required lifecycle phases
are executed per project. This has a special consequence for the clean phase: When Xtext is
generating code into the UI module, and the UI module is cleaned after the runtime module was
build, the code is removed again and compilation would fail. Further, if we clean the project,
we also want that the generated code in the UI project is removed. To solve this build issue,
the runtime project has to congure the maven-clean-plugin to clean up sources also from
the dependend projects. Normally we would need to congure the UI and Tests project to skip
cleaning, but the code is generated to the src-gen folder, which is not recognized as an output
folder. By default, the clean plugin just removes the target folder. Often, project structures
are mavenized to follow a standard layout. Then the sources would be in src/main/java,
82
LWC13 - Submission
and generated sources below target (e.g. target/generated/java). And then we would the
mentioned issue that cleaning must be skipped for the dependend projects.
1 <plugin>
2 <groupId>org.fornax.toolsupport</groupId>
3 <artifactId>fornax-oaw-m2-plugin</artifactId>
4 <executions>
5 <execution>
6 <id>xtext</id>
7 <phase>generate-sources</phase>
8 <goals>
9 <goal>run-workflow</goal>
10 </goals>
11 <configuration>
12 <workflowEngine>mwe2</workflowEngine>
13 <workflowDescriptor>org.eclipse.xtext.example.ql.GenerateQlDsl</workflowDescriptor>
14 <timestampFileName>xtext-generator.timestamp</timestampFileName>
15 <jvmSettings>
16 <fork>true</fork>
17 <jvmArgs>
18 <jvmArg>-Xms100m</jvmArg>
19 <jvmArg>-Xmx700m</jvmArg>
20 <jvmArg>-XX:MaxPermSize=128m</jvmArg>
21 <jvmArg>-Dlog4j.configuration=file:${basedir}/META-INF/log4j.properties</jvmArg
>
22 </jvmArgs>
23 </jvmSettings>
24 </configuration>
25 </execution>
26 </executions>
27 </plugin>
The fornax-oaw-m2-plugin plugin is responsible to invoke the MWE2 workow GenerateQlDsl.mwe2.
The module value in this workow is congured as parameter workflowDescriptor to this plu-
gin.
The plugin is executed in the generate-sources lifecycle phase, which is processed before com-
pilation. You will see this output when it is executed:
1 [INFO] --- fornax-oaw-m2-plugin:3.4.0:run-workflow (xtext) @ org.eclipse.xtext.example.ql
---
2 [INFO] Fornax Model Workflow Maven2 Plugin V3.4.0
3 [INFO] Executing workflow in forked mode.
4 [INFO] Workflow org.eclipse.xtext.example.qls.GenerateQlsDsl finished.
83
LWC13 - Submission
Last but not least the xtend-maven-plugin is congured:
1 <plugin>
2 <groupId>org.eclipse.xtend</groupId>
3 <artifactId>xtend-maven-plugin</artifactId>
4 </plugin>
This plugin compiles the Xtend les to Java les. It does some fancy stu, since Xtend les might
have references to Java classes which are not compiled or might even be not compilable before
Xtend classes are compiled. The plugin therefore scans Xtend les for Java type references and
precompiles stub classes. These stubs are not complete, but enough for Xtend to enable cross
referencing these classes. Later, when Xtend has created the Java sources, they are compiled
together with the other Java sources by the compiler plugin.
5.2.5 QL UI Project POM
The POM for the UI project is surprisingly simply. Nothing special has to be congured.
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/
maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.eclipse.xtext.example.ql</groupId>
7 <artifactId>parent</artifactId>
8 <version>1.0.0-SNAPSHOT</version>
9 <relativePath>../../pom.xml</relativePath>
10 </parent>
11
12 <artifactId>org.eclipse.xtext.example.ql.ui</artifactId>
13 <packaging>eclipse-plugin</packaging>
14 </project>
5.2.6 SDK Feature POM
Not mentioned yet, but we have prepared also a feature project that bundles the QL and QLS
plugins into one feature. The feature project is org.eclipse.xtext.example.ql.sdk, which
can be found in the projects folder of the Git repository.
84
LWC13 - Submission
For feature projects, the packaging type is eclipse-feature. Nothing special has to be con-
gured further.
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/
maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.eclipse.xtext.example.ql</groupId>
7 <artifactId>parent</artifactId>
8 <version>1.0.0-SNAPSHOT</version><!--X-->
9 <relativePath>../../pom.xml</relativePath>
10 </parent>
11
12 <artifactId>org.eclipse.xtext.example.ql.sdk</artifactId>
13 <packaging>eclipse-feature</packaging>
14 </project>
5.2.7 p2 Repository
The result of the build should be a p2 repository, which the user can congure as update site.
The repository project org.eclipse.xtext.example.ql.repository
45
contains a category.xml
le, which references the feature that should be available on this repository:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <site>
3 <feature url="features/org.eclipse.xtext.example.ql.sdk_1.0.0.qualifier.jar" id="org.
eclipse.xtext.example.ql.sdk" version="1.0.0.qualifier">
4 <category name="DSL"/>
5 </feature>
6 <category-def name="DSL" label="DSL"/>
7 </site>
The POM for this project has the packaging type eclipse-repository, besides that it is as
simple as the previous POM.
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/
maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4 <modelVersion>4.0.0</modelVersion>
45
http://tinyurl.com/cho28ox
85
LWC13 - Submission
5 <parent>
6 <groupId>org.eclipse.xtext.example.ql</groupId>
7 <artifactId>parent</artifactId>
8 <version>1.0.0-SNAPSHOT</version><!--X-->
9 <relativePath>../../pom.xml</relativePath>
10 </parent>
11
12 <artifactId>org.eclipse.xtext.example.ql.repository</artifactId>
13 <packaging>eclipse-repository</packaging>
14 </project>
The resulting repository is then available in the target/repository folder.
5.2.8 Continuous Integration
For continuous integration we use the build infrastructure from Cloudbees, which is free for
open-source software. Cloudbees oers Jenkins as CI server, and we have set up a build job for
the DSL projects: https://kthoms.ci.cloudbees.com/job/lwc13-dsl/
86
LWC13 - Submission
This build could be reproduced on any Jenkins server with the following conguration settings:
Create a job of style Maven2/3
Git repository URL:
https://code.google.com/a/eclipselabs.org/p/lwc13-xtext/
Build-trigger: poll repository every 30 minutes
1 */30 * * * *
In the Maven build section, choose a Maven 3 installation
Congure POM: projects/pom.xml
Goals and options: clean install
Alternative settings le (Advanced options): devenv/lwc13.devenv/settings.xml
Add post-build action - archive artifacts: projects/*/target/repository/**
6 Closing Words
Thank you for reading this document. We have been writing it with the intention that it
should provide easy access for rst-time users of Xtext to this powerful Language Workbench.
This explains also the size of the document. We could have written it shorter, if we assumed
more background knowledge of the potential readers, or if we left many things with just short
comments.
Xtext has grown over years, gaining more and more experience from real-life projects that want
to leverage the power of DSLs in integrated environments. We could only touch the surface
of Xtext and Xtend here, and tried to choose the most simple solution. The QL assignment
did t quite well into what Xtext can provide mostly out-of-the-box, but its real power is
unvealed when DSLs have non-standard requirements. Almost every peace in Xtext is highly
customizable and it is one of the most exible frameworks we know of. This exibility comes to
the price of complexity. Xtext is well documented by the reference manual, several blogs show
advanced concepts in detail, and hundreds of projects solve dierent real-world requirements.
Many open projects exist which are worth studying. Learning Xtext does not mean following
a single tutorial, it needs training. The sources and the debugger are valuable friends when
solving issues. The community around Xtext is very helpful and likely the largest of all language
87
LWC13 - Submission
workbenches.
We hope that this document was helpful for you to understand some concepts of Xtext and
gave you the right level of abstraction to learn how to use this tool. If we helped you to decide
to use Xtext or Xtend in your project, then it was worth the eort and we would be pleased to
hear from you!
Karsten Thoms, Johannes Dicks, Thomas Kutz
April 2013
88