Language Workbench Challenge 2013 Xtext Submission

Language Workbench Challenge 2013 Xtext Submission

Language Workbench Challenge 2013

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 different 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, flexible 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 Defining 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 Configuration

. . . . . . . . . . . . . . . . . . . . . . 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 Configuration

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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 Configuration 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 definition 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 Definition

We will use Xtext 2.4.0, which is at the moment of writing the latest official 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 differ 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 file 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 prepackaged already.

We will take the latter approach here and describe the individual steps:

1

2

3

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 finalized 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 file in a directory of your choice.

Example (Linux): cd /opt/local gzip -dc /download/eclipse-SDK-4.2-Xtext-2.3.1-linux-gtk-x86_64.tar.gz | tar xvfp -

The archive will be extracted to a new directory named eclipse. Before unpacking the archive, please ensure that there is no subdirectory named eclipse yet! Different operating

systems may require different 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 files. It is better that the workspace is set to a common encoding to avoid any platform specific 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 file 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-specific languages

(DSLs). It comes as a feature (set of plugins) to the popular Eclipse IDE.

The first thing you will want to do is to create your own domain-specific language (DSL) and specify a grammar for it. The grammar file is a plain text file with “.xtext” filename extension, and the grammar within is defined with a BNF like syntax. While you can use any text editor to modify it, Xtext gives you a specialized editor for grammar files. It is aware of the Xtext language, gives you syntax coloring, code completion, and more. To get a first impression see

11 http://www.xtext.org

9

LWC13 Submission the screenshot of the Xtext grammar file, 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 define 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:

6

7

4

5

1

2

3

8

9

10

11 entity Person { name : String gender : m birthday : Date

} entity Book { title: String authors: Person[] isbnNumber: String

}

10

LWC13 Submission

Note that the Property authors is of type Person, so there can be references between entities.

In the Xtext grammar file you specify how you want to define entities and their properties.

Once you have completed your language, you can do that: define 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 files.

Additionally it generates an editor that is specific to the language you have defined. It knows about your language’s 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 stuff 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 file that adhers to your DSL.

Depending on your language’s type, you could call this text file e.g. a model, a document, a program, or whatever. We will refer to DSL files here as models (files).

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 different. This is where code generation comes in. Xtext creates a skeleton code generator for you. Typically you use that code generator as a starting point to produce e.g. Java source code, documentation in, say, DocBook or Wiki format, overview graphics using GraphViz, or any other stuff you need. Xtext offers 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 suffice for now. The next chapters will show you in more detail how to work with Xtext.

2.2 Create the DSL Projects

Let’s 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 prefix. It is a convention to use a lowercase, dot-separated name.

2. Language name: org.eclipse.xtext.example.ql.QlDsl. This is an identifier for the language, which must be unique and follows a Java full qualified identifier name pattern.

3. Language Extensions: ql. This will be the file extension for DSL files.

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 definition 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 specific 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 files in the /src folder.

• GenerateQlDsl.mwe2: This is a so-called “MWE2 Workflow”. MWE is short for “Modeling

Workflow Engine”, which is a framework that is intended to define processes for code

generation

12

. This file defines the process to generate code for the DSL implementation.

• QlDsl.xtext: This is the file that contains the DSL language definition 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 Defining the Grammar

Open the Grammar file, QlDsl.xtext. In a first step, we will leave out the expression part in

the syntax for simplicity. Enter the following text into the Grammar file

13

:

12

13

14

15

16

10

11

8

9

3

4

1

2

5

6

7

20

21

22

23

17

18

19

24

25

26 grammar generate

/* QL consists of questions grouped in a top-level form construct. */

Form:

/* Abstract rule for elements contained in a Form */

FormElement:

Question

;

"form" name=ID "{" element += FormElement*

"}" ; org.eclipse.xtext.example.ql.QlDsl

qlDsl

/* The top-most container of QL files is a Questionnaire */

Questionnaire: imports+=Import* forms+=Form*;

/* Allows importing of qualified names of types */

Import:

’import’ importedNamespace=QualifiedName; with org.eclipse.xtext.xbase.Xbase

"http://www.eclipse.org/xtext/example/ql/QlDsl"

27

28

29

30

31

32

/**

* - Each question identified by a name that at the same time represents the result of the question.

* - A question has a label that contains the actual question text presented to the user.

* - Every question has a type.

*/

Question: name=ID ":" label =STRING type=JvmTypeReference

;

With the grammar above, the QL language won’t fulfill all requirements of the LWC2013 task.

We will extend the grammar later to meet all requirements. With this grammar a valid model file would look like this:

13 https://gist.github.com/kthoms/4758255

14

LWC13 Submission

10

11

7

8

9

5

6

3

4

1

2 import types.Money

form Box1HouseOwning { hasSoldHouse: "Did you sell a house in 2010?" boolean hasBoughtHouse: "Did you by a house in 2010?" boolean hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean

} sellingPrice: "Price the house was sold for :" Money privateDebt: "Private debts for the sold house: " Money valueResidue: "Value residue: " Money

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 identifier named org.eclipse.xtext.example.ql.QlDsl

14

. It is

derived from another grammar, org.eclipse.xtext.xbase.Xbase. Xbase defines 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 language’s 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 first argument (qlDsl), the package’s 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 define 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 defined in an action or a cross-reference.

• an EEnum

14

That’s 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.

1

2

3

Alternatively an Xtext grammar could be mapped to an existing Ecore metamodel

16

.

Questionnaire: imports+=Import* 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.

1

2

Each QL model will contain zero to many import statements, e.g.: import java.math.BigDecimal

import types.Money

1

2

We will use them to import types used as a question’s 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.

Import:

’import’ importedNamespace=QualifiedName;

1

2

The Import rule is defined to start with the keyword “import”, followed by a QualifiedName.

The QualifiedName rule is not defined in the QlDsl.xtext grammar itself, it is inherited from the Xbase grammar. This rule defines a so-called Datatype Rule, which maps to datatype, in this case EString.

Import:

’import’ importedNamespace=QualifiedName;

1

2

3

4

After the imports section QL forms are defined:

Form:

"form" name=ID "{" element += FormElement*

"}" ;

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 defined in Xtext’s root grammar Terminals. It allows typical Java-style identifiers (beginning with a word character followed by arbitrary many characters, numbers or underscores).

The next step is to define the rule FormElement. It is an abstract rule which will collect the different alternatives of elements that can be contained in a form. In our first 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 effort we are introducing the FormElement already now.

1

2

3

FormElement:

Question

;

Finally, the Question rule is defined. In a first step, Questions are identified by a name, followed by a label string and a reference to a type. Later we will add expressions to compute the value.

1

2

3

Question: name=ID ":" label =STRING type=JvmTypeReference

;

A Question’s 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 defined 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 workflow GenerateQlDsl.mwe2. For this, select the workflow file, open the context menu and select Run As / MWE2 Workflow.

The generator will print some information to the Console, and finally it should print “Done.”.

1

2

3

4

0 [main] INFO lipse.emf.mwe.utils.StandaloneSetup - Registering platform uri ....

...

13727 [main] INFO .emf.mwe2.runtime.workflow.Workflow - Done.

17

LWC13 Submission

After successful execution the projects will be filled 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 configuration. Guice

Injection

20

19

is a famous Dependency 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 plugin, 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

• The Ecore metamodel is generated to file QlDsl.ecore.

LWC13 Submission

• 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 Configuration

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 Configurations and select the node Eclipse Application from the left tree widget and press the icon with the + sign to create a new Launch Config.

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 defined. Select the /src folder and create a new Java class Money in package types:

4

5

6

7

8

1

2

3

12

13

14

15

9

10

11 package types; import java.math.BigDecimal; public class Money { private BigDecimal amount;

} public

} this public

}

Money(BigDecimal amount) {

.amount = amount;

BigDecimal getAmount() { return amount;

Select the /src folder and create a new file “housepurchase.ql”. Once you have created the file 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 files that Xtext should recognize (.ql

files). 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 files. On errors, resource markers are created which can be seen in the editor and the Problems View.

Enter the content for “housepurchase.ql”

23

:

4

5

6

1

2

3 import types.Money

form Box1HouseOwning { hasSoldHouse: "Did you sell a house in 2010?" boolean hasBoughtHouse: "Did you by a house in 2010?" boolean hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean

23 https://gist.github.com/kthoms/5036304

21

LWC13 Submission

9

10

7

8

11

} sellingPrice: "Price the house was sold for :" Money privateDebt: "Private debts for the sold house: " Money valueResidue: "Value residue: " Money

You see now that the editor has recognized our DSL. The language’s keywords are highlighted.

Xtext offers 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 offered with CTRL+SPACE.

• The Outline view

24

presents the structure of the document, and allows quick navigation.

• F3 allows jumping to the definition of an element, which is defined somewhere else. You could try this by selecting “Money” and press F3. At the moment, only the type information 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 question’s 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 define 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 defined in Xtext, thus its elements can be used in any other Xtext grammar by importing or directly extending Xbase via Xtext’s possibility for grammar inheritance. In addition to the grammar, Xbase ships with further infrastructural parts like a compiler, interpreter, 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 defined 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 first 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 defined with the var keyword, whereas for constant values the val keyword is used. Types are derived automatically, so they don’t need to be defined explicitly:

1

2 var myVariable = ’some modifiable value’ 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 first letter’s case which might come in handy in some situations. Large numbers can be written more readable by using underscores to separate digits:

1

2

"a day has " .toFirstUpper() + 86_400_000 + " milliseconds."

// results in: A day has 86400000 milliseconds.

As in Java, Xbase provides if-else-expressions for defining 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 defining behavior depending on the type of a parameter; and last but not least, Xbase allows

the definition 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 define complex domain logic e.g. for the result of a questionnaire directly in its definition. For example, when designing a questionnaire for a test, let’s say to define a person’s stress level, you can write some Xbase code as expression for the last “result” question:

1

2

3 stressLevelResult: "Your Stress-Level: " String (

{ var Integer stressPoints = if (hasTimePressureAtWork) 30 else 0

25 http://www.eclipse.org/Xtext/documentation.html#xbaseLanguageRef_Introduction

23

LWC13 -

6

7

4

5

8

9

)

} stressPoints = stressPoints + daysSleepingBadPerWeek * 3 stressPoints = stressPoints + glassesOfAlcoholPerDay * 12 stressPoints = stressPoints - daysWithSportPerWeek * 2 if (stressPoints>80) "High" else if (stressPoints>40) "Medium" else "Low"

Submission

2.7 Including Expressions into the QL Language

Recall the example of a housowning questionnaire as mentioned in the LWC13 task:

4

5

6

7

8

1

2

3

9

10

11

12

13 import types.Money

form Box1HouseOwning { hasSoldHouse: "Did you sell a house in 2010?" boolean hasBoughtHouse: "Did you by a house in 2010?" boolean hasMaintLoan: "Did you enter a loan for maintenance/reconstruction?" boolean

} if (hasSoldHouse) { sellingPrice: "Price the house was sold for: " Money

} privateDebt: "Private debts for the sold house: " Money valueResidue: "Value residue: " Money (sellingPrice - privateDebt)

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 question’s 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 fulfill the missing requirements our grammar needs to be extended to the following:

26

6

7

4

5

1

2

3

8

9

10 grammar org.eclipse.xtext.example.ql.QlDsl

with org.eclipse.xtext.xbase.Xbase

generate qlDsl "http://www.eclipse.org/xtext/example/ql/QlDsl"

/* The top-most container of QL files is a Questionnaire */

Questionnaire: imports+=Import* forms+=Form*;

/* Allows importing of qualified names of types */

26 https://gist.github.com/kthoms/5114439

24

LWC13 Submission

15

16

17

18

19

20

21

11

12

13

14

22

23

24

25

26

Import:

/* Abstract rule for elements contained in a Form */

FormElement:

Question | ConditionalQuestionGroup

;

’import’

/* QL consists of questions grouped in a top-level form construct. */

Form:

"form" name=ID "{" element += FormElement*

"}" ; importedNamespace=QualifiedName;

36

37

38

39

33

34

35

27

28

29

30

31

32

40

41

42

43

/**

* - Each question identified by a name that at the same time represents the result of the question.

* - A question has a label that contains the actual question text presented to the user.

* - Every question has a type.

* - A question can optionally be associated to an expression:

* this makes the question computed

*/

Question: name=ID ":" label =STRING type=JvmTypeReference expression=XParenthesizedExpression?

;

/**

* Groups questions within a block, optionally made conditional with an if-condition.

*/

ConditionalQuestionGroup: {ConditionalQuestionGroup}

( "if" condition=XParenthesizedExpression)?

"{" element += FormElement*

"}"

;

Compared to the grammar defined in section

2.2

, the follwoing points have changed:

1

2

3

FormElement:

Question | ConditionalQuestionGroup

;

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

4

5

2

3

;

( "if" condition=XParenthesizedExpression)?

"{" element += FormElement*

"}"

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 defined by the question mark ’?’ symbol) which allows for just grouping questions without the necessarity for a condition. The inner elements are again

FormElement s, 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

2

3

Question: name=ID ":" label =STRING type=JvmTypeReference expression=XParenthesizedExpression?

;

After changing the grammar, the implementation has to be regenerated. Run the

GenerateQlDsl.mwe2

workflow 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 defined in a

Java class. When importing such a type via the import statement, it will be available in the questionnaire definition. 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, let’s see how this is

achieved for the Money type:

28

7

8

9

5

6

3

4

1

2 package types; import java.math.BigDecimal; public class Money { private BigDecimal amount; public this

Money (BigDecimal amount) {

.amount = amount;

27

Select it from the Run / Run Configurations 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

14

15

16

17

18

19

20

10

11

12

13

24

25

26

27

28

21

22

23

}

} public BigDecimal getAmount() { return amount;

}

// Implement operators public Money operator_minus (Money other) { return new Money( this .amount.subtract(other.amount));

} public Money operator_plus (Money other) { return new Money( this .amount.add(other.amount));

} public Money operator_multiply (Money other) { return new Money( this .amount.multiply(other.amount));

} public Money operator_divide (Money other) { return new Money( this .amount.divide(other.amount));

}

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), defines how to procede when this operator 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 workflow needs to be executed again (Right click on GenerateQlDsl.mwe2 / Run As.. / MWE2 Workflow). 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 fields 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 Model 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 file

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 type’s 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 field are generated. The field gets only a setter if the value of the Question is not computed by an expression. If the field 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

:

10

11

7

8

9

5

6

3

4

1

2

15

16

17

12

13

14 package org.eclipse.xtext.example.ql.jvmmodel

import com.google.inject.Inject

import java.io.Serializable

import org.eclipse.xtext.common.types.JvmOperation

import org.eclipse.xtext.common.types.util.TypeReferences

import org.eclipse.xtext.example.ql.qlDsl.ConditionalQuestionGroup

import org.eclipse.xtext.example.ql.qlDsl.Question

import org.eclipse.xtext.example.ql.qlDsl.Questionnaire

import org.eclipse.xtext.xbase.XExpression

import org.eclipse.xtext.xbase.XbaseFactory

import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer

import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor

import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder

class QlDslJvmModelInferrer extends AbstractModelInferrer {

@Inject extension JvmTypesBuilder

29 https://gist.github.com/kthoms/5132153

28

LWC13 Submission

53

54

55

56

50

51

52

57

58

59

42

43

44

45

39

40

41

46

47

48

49

32

33

34

35

36

37

38

28

29

30

31

21

22

23

24

25

26

27

18

19

20

@Inject TypeReferences typeReferences def dispatch void infer(Questionnaire element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) { for (form: element.forms) { acceptor.accept(form.toClass( "forms." +form.name))

.initializeLater[

//implements Serializable it.superTypes +=typeReferences.getTypeForName( typeof (Serializable),element, null ) members += toField( "serialVersionUID" ,typeReferences.getTypeForName( "long" ,element)

,[ final = true ^ static = true setInitializer([it.append( "1L" )])

]) val for

} allQuestions = form.eAllContents.filter(

(question: allQuestions) { for (question: allQuestions) { if (question.expression == null ) {

} members += question.toGetter(question.name, question.type) members += question.toSetter(question.name, question.type)

} else { val getter = question.toGetter(question.name, question.type) getter.body = question.expression

members += getter

} members += question.createIsEnabledMethod

typeof (Question)).toList

members += question.toField(question.name, question.type) val allQuestionGroups = form.eAllContents.filter( typeof (ConditionalQuestionGroup)).

toList var groupIndex=0; for (questionGroup: allQuestionGroups) { members += questionGroup.createIsGroupVisibleMethod(groupIndex) groupIndex = groupIndex+1

}

}

}

]

29

LWC13 Submission

60

61

62

63

64

65

66

67 def JvmOperation createIsEnabledMethod (Question question) { question.toMethod( "is" +question.name.toFirstUpper+ "Enabled" , typeReferences.

getTypeForName( "boolean" , question, null )) [ body = [it.append( ’’’return «question.expression == null»;’’’ )]

}

]

68

73

74

75

76

77

69

70

71

72

}

/** Create a method <code>public boolean isGroup[groupIndex]Visible ()</code>.*/ def JvmOperation createIsGroupVisibleMethod (ConditionalQuestionGroup group, int groupIndex) { group.toMethod( "isGroup" +groupIndex+ "Visible" , typeReferences.getTypeForName( "boolean" , group, null )) [ if (group.condition != null ) { body = group.condition

} else { body = [it.append( ’’’return true;’’’ )]

}

]

}

5

6

7

1

2

3

4

Now lets take a deeper look at the implementation: class QlDslJvmModelInferrer extends AbstractModelInferrer {

@Inject extension JvmTypesBuilder

@Inject TypeReferences typeReferences def dispatch void infer(Questionnaire element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {

...

}

}

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 first argument can be of type Questionnaire, and not of the base type EObject, like defined in the infer() method that is definied in IJvmModelInferrer.

The implementation uses two services, which are injected as members into the class:

• The JvmTypesBuilder offers factory and builder functions to create instances of JVM

30

for (form: element.forms) { acceptor.accept(form.toClass( "forms." +form.name))

.initializeLater[

...

]

}

LWC13 Submission

Model types. The additional keyword extension has the effect, that the methods of the JvmTypesBuilder become so-called extension methods. This means, the functions become implicitly available as additional methods on the first 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 qualified

Java class names through its getTypeForName() methods.

3

4

1

2

5

6

Let’s 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 first derive a Class instance for each

Questionnaire element in package forms. JVM Model Inference is executed in two phases: In the first 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.

it.superTypes +=typeReferences.getTypeForName( typeof (Serializable),element, null )

1

2

3

4

5

6 members += toField( "serialVersionUID" ,typeReferences.getTypeForName( "long" ,element),

[ final = true ^ static = true setInitializer([it.append( "1L" )])

])

We want to make the resulting Java class serializable. This is optional, but better style. Therefore 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 identifier 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 field with the value “1” of type long.

val allQuestions = form.eAllContents.filter( typeof (Question)).toList

1

2

3

4

5 for (question: allQuestions) { members += question.toField(question.name, question.type)

}

31

LWC13 Submission

All Question instances from the resource are bound to the final 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 field. Here we see the effect of the extension keyword: It seems that toField is actually a method of type Question, but it is a method of the JvmTypesBuilder class.

8

9

10

11

3

4

5

1

2

6

7 for (question: allQuestions) { if (question.expression == null ) { members += question.toGetter(question.name, question.type) members += question.toSetter(question.name, question.type)

} else { val getter = question.toGetter(question.name, question.type)

}

...

getter.body = question.expression

members += getter

}

The next loop creates the accessor methods for the fields. We could have done this in the previous loop also, but it is better style to declare the fields first, 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 hasn’t one, it is a simple field 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 offer a setter method. The field needs to be read-only. The getter method does not simply return the value of a field. Instead, the method has to evaluate the expression. Thus, we assign the expression as body of the method.

1

2

3

4

5

6

7

8 for (question: allQuestions) {

...

members += question.createIsEnabledMethod

}

...

def JvmOperation createIsEnabledMethod (Question question) { question.toMethod( "is" +question.name.toFirstUpper+ "Enabled" ,

32

LWC13 Submission

9

10

11 } typeReferences.getTypeForName( "boolean" , question, null )) [ body = [it.append( ’’’return « question.expression == null»;’’’ )]

]

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 first example where we make use of Xtend’s Rich String feature (the text between the three single quotes ”’), which is later heavily used in the code generator templates.

6

7

4

5

8

1

2

3 val allQuestionGroups = form.eAllContents.filter( typeof (ConditionalQuestionGroup)).toList

var groupIndex=0; for (questionGroup: allQuestionGroups) {

} members += questionGroup.createIsGroupVisibleMethod(groupIndex) groupIndex = groupIndex+1

9

10

11

12

13

14

15

16 def JvmOperation createIsGroupVisibleMethod (ConditionalQuestionGroup group, int groupIndex)

{ group.toMethod( "isGroup" +groupIndex+ "Visible" , typeReferences.getTypeForName( "boolean" , group, null )) [ if (group.condition != null ) { body = group.condition

} else { body = [it.append( ’’’return true;’’’ )]

}

}

]

We now filter all ConditionalQuestionGroup instances from the Questionnaire and loop over them. For each of them, a method is<QUESTIONGROUPINDEX>Visible() is produced. Unfortunately, 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 won’t 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 configured by default as implementation of the IScopeProvider interface.

For our use case, the default behavior is already sufficient. Through the JVM model inference the implicit “this” variable is already bound to the class representing the form, and thus the fields 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. Therefore a reference implementation has been developed which was coded to large degree manually. 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 container) like Glassfish, 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 configuration

(WebContent/WEB-INF/faces-config.xml)

• Images

(WebContent/resources/default/img/*)

• Page Templates

(WebContent/resources/default/templates/*)

After describing some necessary configuration

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 Configuration

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 Workflow

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 configuration

- LWC13 Runtime import existing project lwc13-xtext examples

QLTest

3.1.3 Main Layout

The logical entry to the web application is the welcome file WebContent/index.xhtml declared 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 application’s layout by using JSF’s XHTML templating

34

.

10

11

8

9

12

13

14

3

4

1

2

5

6

7

<?xml version=’1.0’ encoding=’UTF-8’ ?>

<!

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >

< html xmlns = "http://www.w3.org/1999/xhtml" xmlns :h= "http://java.sun.com/jsf/html" xmlns :ui= "http://java.sun.com/jsf/facelets" >

< body >

<ui:composition template= "/resources/default/templates/defaultLayout.xhtml"

<ui:define name

Hello World!

</ui:define>

</ui:composition>

= "content" >

>

34 http://docs.oracle.com/javaee/6/javaserverfaces/2.1/docs/vdldocs/facelets/

37

LWC13 Submission

15

16

17

</ body >

</ html >

3

4

1

2

5

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.

<ui:composition template= "/index.xhtml" >

<ui:define name = "content" >

...my xhtml content

</ui:define>

</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”) defined 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 definition and the page contents separated from each other. The

WebContent/index.xhtml

defines the main composition of the application’s structural layout and content of pages as described in section

3.1.3

. Layout template defintions 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.

3

4

1

2

...

<ui:composition template= "/resources/default/templates/defaultLayout.xhtml" >

...

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 defined facelets:insert section with name ’content’

38

LWC13 Submission within the template or one of its parents for proper composition. In our reference implementation it is declared in /resources/default/templates/defaultLayout.xhtml.

11

12

13

14

15

9

10

7

8

1

2

3

4

5

6

19

20

21

22

16

17

18

23

24

25

26

<!

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >

< html xmlns = "http://www.w3.org/1999/xhtml" xmlns :h= "http://java.sun.com/jsf/html" xmlns :ui= "http://java.sun.com/jsf/facelets" >

<h: head >

< title ><ui:insert name = "title" >LWC 2013 Xtext</ui:insert></ title >

</h: head >

< body >

< div id = "header" >

<ui:insert name = "header" >

<ui:include src = "/resources/default/templates/header.xhtml" />

</ui:insert>

</ div >

< div id = "content" >

<ui:insert name = "content" >

Content area. Compose by use of tag facelet:define & name= "content" .

</ui:insert>

</ div >

< div id = "footer" >

<ui:insert name = "footer" >

<ui:include src = "/resources/default/templates/footer.xhtml" />

</ui:insert>

</ div >

</ body >

</ 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 fixed source src.

Our structural layout definition which is composed by using

/resources/default/templates/defaultLayout.xhtml

as template in

WebContent/resources/default/ has a Cascading Style Sheet (CSS) layouting can be done.

35

where fine grained

35 http://de.wikipedia.org/wiki/Cascading_Style_Sheets

39

LWC13 Submission

The applications style sheets consist of 2 files.

• container.css - defines styles for main page elements, basically we took the style of http://www.itemis.de/

• base.css - defines 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 definitions 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 specific content which enables the questionnaire application specified 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 template and will be the template for concrete forms in our application

• WebContent/forms/HouseOwning.xhtml - this file will implement 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 complex types (e.g. instances of custom type Money)

Form

The form of our reference application consists of different areas where each defines 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 different parts of the questionnaire are defined 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.

4

5

6

7

8

1

2

3

9

10

11

12

< div class = "ym-grid" >

<h:outputLabel styleClass= value =

"ym-g33 ym-gl"

"Did you sell a house in 2010?"

<h:selectBooleanCheckbox styleClass= id

</ div >

= "chkHasSoldHouse" value

</h:selectBooleanCheckbox>

=

<f:ajax execute= "chkHasSoldHouse"

/>

"ym-g50 ym-gl"

"#{houseOwning.hasSoldHouse}" render= "grp_hasSoldHouse_hasBoughtHouse" />

>

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

2

3

< label class = "ym-g33 ym-gl" >

Did you sell a house in 2010?

</ label >

The JSF html:selectBooleanCheckbox will be translated in a more complex HTML:input tag.

The most interesting thing is the action definition 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.

5

6

7

8

1

2

3

4

< input id = "houseOwningForm:chkHasSoldHouse" class = "ym-g50 ym-gl" type = "checkbox" onclick = "mojarra.ab(

)" this,event,’valueChange’,’houseOwningForm:chkHasSoldHouse’,’houseOwningForm: grp_hasSoldHouse_hasBoughtHouse’ checked = "checked" name = "houseOwningForm:chkHasSoldHouse" >

</ 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 responsible for the shown layout.

• ym-grid - defines that childs should be grouped in a table

• ym-g*number - defines the width of an element

• ym-gl - defines that the element should float 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.

3

4

5

1

2

6

< div class = "ym-grid" >

<h:outputLabel value =

<h:inputText styleClass styleClass =

= "ym-g60 ym-gl"

"Price the house was sold for:"

"ym-g33 ym-gl"

/> id = "inSellingPrice"

42

LWC13 Submission

9

10

7

8

11

12 value

</ div >

= "#{houseOwning.sellingPrice.amount}"

<f:ajax event = "keyup" execute= "inSellingPrice" render= "grp_ValueReside" />

</h:inputText>

>

As you can see in the figure above the definition 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

2

3

4

5

8

9

6

7

< input id = "houseOwningForm:inSellingPrice" class = "ym-g33 ym-gl" type = "text" onkeyup = "mojarra.ab( this,event,’keyup’,’houseOwningForm:inSellingPrice’,’houseOwningForm:grp_ValueReside

)" value = "0" name = "houseOwningForm:inSellingPrice" >

</ 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 official 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 beneficial when writing code generators.

38 http://www.eclipse.org/xtend/documentation.html

43

LWC13 Submission

3

4

5

1

2

6 def someMethod() { var myQuestion = ’where do we go?’ myQuestion = myQuestion.toFirstLower

val myAnswer = 42

’The answer for question ’ +myQuestion+ ’ is: ’ +myAnswer

}

In Xtend, a method is defined 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 specified explicitly. The keyword var defines a variable; constant values are defined 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 defines the return value and also the return type of the method.

6

7

4

5

1

2

3

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: def square(Integer input) { input * input

} def useExtension() {

16.square

// returns 256

}

What Xtend basically does is changing the syntax of how methods are called. Instead of writing toLastUpper(“Hello”)

, Xtend always offers the alternative to use the first 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 field defining 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

2

3

4 class MyGenerator implements IGenerator{

@Inject extension IJvmModelAssociations

...

}

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 configured in the Guice module.

3

4

5

1

2

8

9

6

7

10

11

12

13

A further useful feature of Xtend is polymorphic dispatching. Using the dispatch keyword on multiple methods with identical signatures has the effect 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.

def dispatch doSomething(SubTypeA input) {

// do something SubTypeA specific

} def dispatch doSomething(SubTypeB input) {

// do something SubTypeB specific

} def useDispatching() { val SuperType a = new SubTypeA() val SuperType b = new SubTypeB() a.doSomething + b.doSomething

}

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 different 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.

1

2

Xtend offers the possibility to define 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 find 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: def htmlContent(List<String> contents) ’’’

<html>

45

LWC13 Submission

9

10

11

7

8

12

5

6

3

4

<body>

«FOR content: contents BEFORE ’ <p> ’ SEPARATOR ’ <br/> ’ AFTER ’ </p> ’»

«IF content.length > 10»

Large Content: «content.toFirstUpper»

«ELSE»

Small Content: «content.toFirstUpper»

«ENDIF»

«ENDFOR»

</body>

</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 specified.

Calling the example method with [’hello’, ’Some more text’] results in:

7

8

5

6

3

4

1

2

<html>

<body>

<p>

Small Content: Hello<br/>

Large Content: Some more text

</p>

</body>

</html>

1

2

3

4

5

6

7

Last but not least, Xtend offers 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: def getTypeName(Number input) { switch (input) {

Integer: "It is an Integer!"

Float: "It is a Float!" default : "It is some other number type."

}

}

Now as you know the basic concepts of Xtend, let’s finally 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 different code generators for different 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

2

3

4

5

6

7

8

9 package org.eclipse.xtext.generator; public interface IGenerator {

/**

* @param input - the input for which to generate resources

* @param fsa - file system access to be used to generate files

*/ public void doGenerate(Resource input, IFileSystemAccess fsa);

}

3.3.1 Dispatcher template

The code generator is invoked with a Resource instance, which holds a Questionnaire instance. 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:

6

7

4

5

1

2

3

8

9

10

11

12 package org.eclipse.xtext.example.ql.generator; import javax.inject.Inject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.generator.IFileSystemAccess; import org.eclipse.xtext.generator.IGenerator; import org.eclipse.xtext.xbase.compiler.JvmModelGenerator;

@SuppressWarnings ( "restriction" ) public class Root implements IGenerator {

@Inject

47

LWC13 Submission

17

18

19

13

14

15

16

}

JvmModelGenerator jvmModelGenerator; public void doGenerate(Resource input, IFileSystemAccess fsa) {

// dispatch to other generators jvmModelGenerator.doGenerate(input, fsa);

}

As a first 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.

1

2

3

4

Next, Xtext has to know that Root is the template that has to be invoked as generator implementation. 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 configuration that binds the IGenerator interface to the Root class.

@Override public Class<?

extends IGenerator> bindIGenerator() { return Root.

class ;

}

Now we are ready to add additional templates and register them in the Root class.

3.3.2 Output Configuration 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 derived 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

2

3

4 package org.eclipse.xtext.example.ql.generator; import java.util.Set;

39 http://xtextcasts.org/episodes/15-output-configurations

48

LWC13 Submission

9

10

11

12

13

14

15

7

8

5

6

19

20

21

22

16

17

18

28

29

30

31

23

24

25

26

27 import org.eclipse.xtext.generator.OutputConfiguration; import org.eclipse.xtext.generator.OutputConfigurationProvider; public class JSFOutputConfigurationProvider extends OutputConfigurationProvider {

} public final

/** public Set<OutputConfiguration> getOutputConfigurations() {

}

* @return a set of {@link OutputConfiguration} available for the generator

*/

Set<OutputConfiguration> outputConfigurations = super

.getOutputConfigurations();

OutputConfiguration webContent = new OutputConfiguration(WEB_CONTENT); webContent

.setDescription( "Read-only Output Folder for web generated application artifacts" ); webContent.setOutputDirectory( "./WebContent" ); webContent.setOverrideExistingResources( true ); webContent.setCreateOutputDirectory( true ); webContent.setCleanUpDerivedResources( true ); webContent.setSetDerivedProperty( true ); outputConfigurations.add(webContent); return

String WEB_CONTENT = outputConfigurations;

"WebContent" ;

The interface OutputConfiguration provides several options to configure the behavior of the so called Outlet. We use the defaults as in OutputConfigurationProvider except its name, description and outputDirectoy.

1

2

3

4

5

Our JsfOutputConfigurationProvider can be bound in the QlDslRuntimeModule by overriding/extending the method configure.

@Override public void configure(Binder binder) { super .configure(binder); binder.bind(IOutputConfigurationProvider.

class )

.to(JSFOutputConfigurationProvider.

class ).in(Singleton.

class );

}

After this step we can refer to the additional OutputConfiguration in generators by use of the constant WEB_CONTENT defined 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 defintion 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 figure above, we can finish the wizard so that the class shown in the following listing will be created in our project.

7

8

9

10

1

2

3

4

5

6 package org.eclipse.xtext.example.ql.generator

import org.eclipse.xtext.generator.IGenerator

import org.eclipse.emf.ecore.resource.Resource

import org.eclipse.xtext.generator.IFileSystemAccess

class JSFGenerator implements IGenerator { override doGenerate(Resource input, IFileSystemAccess fsa) { 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.

14

15

16

17

11

12

13

18

19

20

21

9

10

6

7

8

4

5

1

2

3

} package org.eclipse.xtext.example.ql.generator; import javax.inject.Inject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.generator.IFileSystemAccess; import org.eclipse.xtext.generator.IGenerator; import org.eclipse.xtext.xbase.compiler.JvmModelGenerator; public class Root implements IGenerator {

@Inject

JvmModelGenerator jvmModelGenerator;

@Inject

JSFGenerator jsfGenerator; public void doGenerate(Resource input, IFileSystemAccess fsa) {

}

// dispatch to other generators jvmModelGenerator.doGenerate(input, fsa); jsfGenerator.doGenerate(input, fsa);

1

2

Now it is time to add some functionality to the JSFGenarator. Open the file JSFGenarator.xtend

and go to the doGenerate extension which is responsible to generate artifacts. Delete the autogenerated body of the extension - initially it just throws an UnsupportedOperationException

- and add the following lines as first statements to prevent execution of the generator if the file extension does not fit.

if (input.URI.fileExtension!= "ql" ) return

1

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.

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

3

4

1

2 class JSFGenerator implements IGenerator{

@Inject extension JSFOutputConfigurationProvider

}

...

After this we have the possibility to use the WEB_CONTENT outlet constant as described in section

3.3.2

.

1

2

3

4

8

9

10

5

6

7

The following section

3.3.3

will describe the different 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 different artifacts in different Xtend files. Our sample is a very simple one, so we will add a new extension definition derived from the sample below to the

JSFGenerator.xtend

class which encapsulates the logic to generate a single artifact.

def generate_Artifact (EObject modelInfo)

’’’<?xml version=’1.0’ encoding=’UTF-8’ ?>

<!

-- @generated ->

<!

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/ xhtml1/DTD/xhtml1-transitional.dtd" >

< html xmlns = "http://www.w3.org/1999/xhtml" xmlns :h= "http://java.sun.com/jsf/html" xmlns :ui= "http://java.sun.com/jsf/facelets" >

... artifact content

</ html >

’’’

To get a valid XHTML page we have to generate the DOCTYPE and HTML tag into each

XHTML file. 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 defined in our model. The generated index page should be saved in a file called index.xhtml within a subfolder ’generated/forms/’ of the WEB_CONTENT outlet created in section

3.3.2

.

1

2

3

4

5 class JSFGenerator implements IGenerator{

@Inject extension JSFOutputConfigurationProvider

@Inject extension QlDslExtensions override doGenerate(Resource input, IFileSystemAccess fsa) {

52

LWC13 Submission

10

11

12

13

14

8

9

6

7

15

16

17

}

}

...

if (input.URI.fileExtension!= "ql" ) return

// model root val questionnaire = input.contents.head as Questionnaire

// generate index page with links to generated forms val contentIndex = generate_FormIndex(questionnaire.forms) val fileNameIndex = "generated/forms/index.xhtml" fsa.generateFile(fileNameIndex,WEB_CONTENT, contentIndex)

...

10

11

8

9

12

13

3

4

1

2

5

6

7

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 defined within the same QL resource to ensure generation of a correct form index page.

def generate_FormIndex (List<Form> forms)

’’’...

<ui:composition template="/index.xhtml">

<ui:define name="content">

«FOR elem: forms SEPARATOR "<br/>"»

<h:outputLink value="«elem.name».jsf">«elem.name»</h:outputLink>

«ENDFOR»

’’’

</ui:define>

</ui:composition>

...

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 defines 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 files in a single file 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:

28

29

30

31

25

26

27

19

20

21

22

23

24

32

33

34

35

14

15

16

17

18

9

10

11

12

13

1

2

3

4

7

8

5

6 class QlDslExtensions {

@Inject extension IJvmModelAssociations

/**

* Computes the FormElements which are accessed by the expression of a Question.

*/ def Iterable<FormElement> getDependentElementsWithExpression (Question q) { if (q.expression != null ) return emptyList

// The JvmField which is inferred from a Question val JvmField field = q.jvmElements.filter( typeof (JvmField)).head

// Get all FormElements which have an expression val Iterable<FormElement> allFormElementsWithExpression = q.form.eAllContents

.filter( typeof (FormElement))

.filter[it.expression!=

.toSet

null ]

// search the expressions of the form elements which call the JvmField field in a feature call val result = allFormElementsWithExpression.filter[ val exp = it.expression

if (exp instanceof XFeatureCall) {

// a simple expression e.g. ’(XFeatureCall)’

(exp as XFeatureCall).feature.simpleName == field.simpleName

} else {

// a complex expression e.g. ’(XFeatureCall1 - XFeatureCall2)’ val xfeaturecalls = exp.eAllContents.filter( typeof (XFeatureCall)) xfeaturecalls.exists[ feature.simpleName == field.simpleName

]

}

]

} return result

54

LWC13 Submission

62

63

64

65

59

60

61

66

67

68

69

70

74

75

76

77

71

72

73

50

51

52

53

47

48

49

54

55

56

57

58

40

41

42

43

44

45

46

36

37

38

39

}

/**

* Creates an id for the given domain object.

*/ def String getId (EObject o) { switch (o) {

ConditionalQuestionGroup: "group" +allConditionalGroups(o).indexOf(o)

Question: o.name

Form: o.name.toLowerCase

}

}

/** def private allConditionalGroups (EObject ctx) {

}

/**

} def getForm(EObject question) {

EcoreUtil2::getContainerOfType(question,

}

* Get all ConditionalGroups underneath the given context.

*/ ctx.form.eAllContents.filter(

* Get the parent form’s name

*/ elem.form.name.toFirstLower

/**

*/ typeof def getFormName(FormElement elem){

(ConditionalQuestionGroup)).toList

* Get the Form container of the given question.

typeof

/**

* Returns the expression assigned to a FormElement, dependent on subtype for FormElement.

}

*/ def getExpression (FormElement elem) { switch (elem) {

}

Question: elem.expression

ConditionalQuestionGroup: elem.condition

(Form)) as Form

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 define which parts of the web page need to be updated when a particular question is answered by the user. For this, first the JVM field 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 filtered in a way that only the ones remain whose expression contains the JVM fields name (19-32). The result of this filtering is the method’s return value (line 33).

3.4 Testing the Questionnaire Application

It’s 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 fly when the QL model

gets modified. The generated artifacts are located in folder WebContent/generated/forms.

Into this folder also an index.xhtml file is placed which you can use as starting point for the questionnaire application. Right-click on the index file and press Run As / Run On Server. In the next dialog choose the Tomcat server which you should have configured 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 Configured 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 it’s show time! You can modify your QL model and save it. The XHTML files will be regenerated on the fly. 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 define a language for styling and layout information can be accomplished in our language workbench. The language QLS should allow for defining the following information:

• Grouping of question forms into pages, sections and subsections

• Navigation links between pages

• Styling of question texts by defining font style, font weight, font family and color

• Defining 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 define with QLS:

6

7

4

5

1

2

3

8

9

10

11

12

16

17

18

19

20

13

14

15 page HouseOwningPage { section house uses Box1HouseOwning { question hasBoughtHouse [ font-style : "italic" ] question valueResidue [ font-weight : "bold" font-color : "#2233FF" font-family : "Arial"

]

} section garage uses GarageOwning { question hasBoughtGarage [ widget : Radio [ "Yepp" , "Nope" ]]

} navigation {

CarOwningPage

}

} page CarOwningPage uses CarOwning {

// ...

}

This example model defines two pages. The first page consists of two sections, one for the question form Box1HouseOwning which was defined in chapter

2

and one for a further form called GarageOwning. The form to be rendered inside a page or a section is specified by the uses keyword. This definition 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 defined as in lines 3-9 and 11. The navigation keyword allows to define 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 file QlsDsl.xtext. We define the content of this file as the following:

13

14

15

16

10

11

12

17

18

19

20

21

5

6

7

8

9

3

4

1

2

25

26

27

28

22

23

24

29

30

31

32

33 grammar org.eclipse.xtext.example.qls.QlsDsl

with org.eclipse.xtext.common.Terminals

generate qlsDsl "http://www.eclipse.org/xtext/example/qls/QlsDsl" import "http://www.eclipse.org/xtext/example/ql/QlDsl" as ql

QuestionnaireStyleModel: pages+=Page*;

Page:

"page" name=ID ( "uses" form=[ql::Form|ID])?

"{" element+=PageElement* navigation=Navigation?

"}"

;

PageElement:

QuestionStyling | Section

;

QuestionStyling:

"question" question=[ql::Question] styling+=StyleInformation?

;

StyleInformation: {StyleInformation}

"[" (

( "font-style:" fontStyle=STRING)? &

( "font-weight:" fontWeight=STRING)? &

( "font-color:" fontColor=STRING)? &

( "font-family:" fontFamily=STRING)? &

( "widget:" widget=Widget)?

) "]"

;

59

LWC13 Submission

39

40

41

42

43

44

45

46

36

37

38

34

35

Widget: {Widget} widgetType=( "Radio" | "DropDown" | "CheckBox" | "Text" | "Slider" ) ( "[" labels+=STRING ( "," labels

+=STRING)* "]" )?

;

Section:

;

"section" name=ID ( "uses" form=[ql::Form|ID])?

"{" element+=PageElement*

"}"

Navigation: {Navigation}

;

"navigation" "{" (nextPage+=[Page|ID])+ "}"

After reading chapter

2.2

you should be familiar with the concepts of Xtext grammar definitions and understand most parts of the QLS grammar. One new concept represented in the QLS grammar is the one of unordered lists:

6

7

8

9

1

2

3

4

5

StyleInformation: {StyleInformation}

"[" (

( "font-style:" fontStyle=STRING)? &

( "font-weight:" fontWeight=STRING)? &

( "font-color:" fontColor=STRING)? &

( "font-family:" fontFamily=STRING)? &

( "widget:" widget=Widget)?

) "]"

;

The styling information can be defined 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

2

3

Navigation: {Navigation}

"navigation" "{" (nextPage+=[Page|ID])+ "}"

;

60

LWC13 Submission

Here, nextPage is a reference to an existing page description which may even be defined in a different file. ID defines which attribute should be used for the reference’s name. Xtext automatically 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 define references to elements defined in other langauges than the own, the reference needs to be qualified. An example in QLS is the reference to questions or forms defined in a QL model.

To express this on the grammar level, the QL DSL needs first 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 qualified by using a double colon (::):

1

2

3

QuestionStyling:

"question" question=[ql::Question] styling+=StyleInformation?

;

This grammar rule allows for refering all question elements defined in any QL model in our test project. However, since there is always an unambiguous form specified for a page or a section, only the questions defined 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:

3

4

1

2

5

6

7

8

9

10 class QlsDslScopeProvider extends AbstractDeclarativeScopeProvider {

}

@Inject extension QlsDslExtensions def IScope scope_QuestionStyling_question(EObject context, EReference ref) {

}

Scopes::scopeFor(

);

EcoreUtil2::getAllContentsOfType(context.form, typeof (Question))

The method scope_QuestionStyling_question is always invoked whenever the visible elements 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 proposed. The method’s 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 defined 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 corresponding extension method in QlsDslExtensions. There, we use Xtend’s polymorphic dispatching feature (see also section

3.2

):

7

8

5

6

9

10

11

12

13

14

15

1

2

3

4 class QlsDslExtensions {

} def dispatch Form getForm(Section section) {

} if (section.form != null ) { section.form

} else { section.eContainer.form

} def dispatch

} page.form

getForm(Page page) {

Note, that for the first 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 automatically. The calculated form is used in the scope provider as input for the helper method getAllContentsOfType which is used to collect all questions defined in the given form.

Now it’s 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 file with the

file extension .qls.

4.2 QLS Code Generator

So far, our QLS models have no effect on the generated output. The first step is to think about which artifacts are to be generated from a QLS model. The QLS model contains two kinds of information. The first is layout information: A page consists of one or more forms. Recall that for each form two XHTML files 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 file which is composed of all specified 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 file

62

LWC13 Submission

• Generate one XHTML file wiring all form XHTMLs together and reference CSS file

Let’s take the following QLS model as a reference example:

18

19

20

21

15

16

17

10

11

12

13

14

3

4

5

1

2

8

9

6

7 page HouseOwningPage { section house uses HouseOwning { question hasBoughtHouse [ font-style : "italic" ] question valueResidue [ font-weight : "bold" font-color : "#2233FF" font-family : "Verdana"

]

} section garage uses GarageOwning { question hasBoughtGarage [ widget : Radio [ "Yepp" , "Nope" ]]

} navigation {

CarOwningPage

}

} page CarOwningPage uses CarOwning { question hasSoldCar [ font-color : "green" ] question hasBoughtCar [ font-color : "red" ]

}

The intended generated file structure is visualized in the screenshot on the left. The files in the folder 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 files under the folder pages and the CSS files in resources/default/css/generated. The file pages/index.xhtml

serves as root page containing the link to the first actual page to be presented.

The page HouseOwningPage contains two sections using the forms HouseOwning and GarageOwning, thus the generated file pages/HouseOwningPage.xhtml wires the two base files forms/HouseOwningBase.xhtml

and forms/GarageOwningBase.xhtml together:

63

LWC13 Submission

4

5

6

7

8

1

2

3

12

13

14

15

16

9

10

11

< html xmlns = "http://www.w3.org/1999/xhtml" xmlns :ui= "http://java.sun.com/jsf/facelets" xmlns :h= "http://java.sun.com/jsf/html" xmlns :f= "http://java.sun.com/jsf/core" >

<h: head ></h: head >

<ui:composition template= "/index.xhtml" >

<ui:define name = "content" >

<h:outputStylesheet library= "default/css/generated" name = "HouseOwningPage.css" />

< div ><ui:include src = "/generated/forms/HouseOwningBase.xhtml" /></ div >< p />

< div ><ui:include src = "/generated/forms/GarageOwningBase.xhtml" /></ div >

< div >

<h:outputLink value = "CarOwningPage.jsf" >CarOwningPage</h:outputLink>

</ div >

</ui:define>

</ui:composition>

</ html >

3

4

5

1

2

8

9

6

7

10

The two pages are referenced in lines 9 and 10. Line 12 defines the link to the next page as defined in the navigation section of the QLS model. Line 8 specifies which CSS file is to be used to render the site’s elements. The CSS file for the house owning page contains all corresponding styling information which are linked to the corresponding label elements by their ids:

#houseowning\:lblHasBoughtHouse { font-style: italic;

}

#houseowning\:lblValueResidue { color: #2233FF; font-family: Verdana; font-weight: bold;

}

#garageowning\:lblHasBoughtGarage {

}

When JSF converts from XHTML each element gets a unique (full qualified) 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 clarified, the code generator itself can be written. Here again we use Xtend (see section

3.2

):

64

LWC13 Submission

23

24

25

26

20

21

22

27

28

29

30

31

11

12

13

14

8

9

10

3

4

5

1

2

6

7

15

16

17

18

19

35

36

37

38

32

33

34

39

40

41

42

43

44 class QlsDslGenerator implements IGenerator {

@Inject extension JsfOutputConfigurationProvider

@Inject extension QlDslExtensions override void

} if val for (page: styleModel.pages) {

} val contentIndex = generateIndexPage(styleModel.pages.get(0)) fsa.generateFile( "generated/pages/index.xhtml" ,WEB_CONTENT, contentIndex) def

(input.URI.fileExtension!= return val cssContent = generateCssFile(page); val fsa.generateFile(cssFileName, WEB_CONTENT, cssContent) val val doGenerate(Resource input, IFileSystemAccess fsa) { styleModel = input.contents.head

cssFileName = "resources/default/css/generated/" xhtmlContent = generateXhtmlFile(page); xhtmlFileName =

"qls" ) as

"generated/pages/"

QuestionnaireStyleModel

+page.name+

+page.name+

".xhtml" fsa.generateFile(xhtmlFileName, WEB_CONTENT, xhtmlContent)

".css"

<!-- @generated -->

«FOR styleInfo: page.eAllContents.filter(typeof(StyleInformation)).toList»

«styleInfo.id» {

}

«ENDFOR»

’’’ def generateCssFile(Page page) ’’’

«IF styleInfo.fontColor != null»color: «styleInfo.fontColor»;«ENDIF»

«IF styleInfo.fontFamily != null»font-family: «styleInfo.fontFamily»;«ENDIF»

«IF styleInfo.fontStyle != null»font-style: «styleInfo.fontStyle»;«ENDIF»

«IF styleInfo.fontWeight != null»font-weight: «styleInfo.fontWeight»;«ENDIF» generateXhtmlFile(Page page) ’’’

<!-- @generated -->

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">

<h:head></h:head>

<ui:composition template="/index.xhtml">

<ui:define name="content">

<h:outputStylesheet library="default/css/generated" name="«page.name».css" />

65

LWC13 Submission

79

80

81

82

83

84

85

75

76

77

78

60

61

62

63

64

56

57

58

59

50

51

52

53

54

55

68

69

70

71

65

66

67

72

73

74

45

46

47

48

49

«IF page.form != null»

<div class="highlight_section"><ui:include src="/generated/forms/«page.form.name»

Base.xhtml" /></div>

«ELSE»

«FOR section: page.eAllContents.toList.filter(typeof(Section)).toList SEPARATOR ’ <p

/> ’»

<div class="highlight_section"><ui:include src="/generated/forms/«section.form.name

»Base.xhtml" /></div>

«ENDFOR»

«ENDIF»

«IF page.navigation != null»

<form>

<div class="highlight_section ym-grid">

<h:outputLabel styleClass="lvl1Lbl ym-gl" id="lblNavigation" value="Next pages:"/>

«FOR nextPage: page.navigation.nextPage»

<h:outputLink styleClass="lvl1Lbl ym-gl" value="«nextPage.name».jsf">

«nextPage.name»

</h:outputLink>

«ENDFOR»

</div>

</form>

</ui:composition>

</html>

’’’

«ENDIF»

</ui:define> def generateIndexPage(Page page) ’’’

<?xml version=’ 1.0

’ encoding=’ UTF-8 ’ ?>

<!-- @generated -->

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/ xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets">

<ui:composition template="/index.xhtml">

<ui:define name="content">

<h:outputLink value="«page.name».jsf">«page.name»</h:outputLink>

</ui:define>

</ui:composition>

</html>

’’’

66

LWC13 Submission

86

87

88

89

90

} def getId(StyleInformation styleInfo) { val question = (styleInfo.eContainer

as QuestionStyling).question

"#" +question.form.id+ "\\:lbl" +question.id.toFirstUpper

}

The doGenerate method is similar to the one of the JSFGenerator from section

3.3.3

. It basi-

cally defines file names and delegates to other methods for defining the generated files’ contents

(generateCssFile, generateXhtmlFile, generateIndexPage). We won’t go into details here.

Instead let’s test the generated application.

For this, you can basically follow the instructions given in section

3.4

. To see some genrated pa-

ges you first need to define a QLS model. To start the application you can use the index.xhtml

file in folder WebContent/generated/pages. Right-click on this file 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

68

LWC13 Submission

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 defined in the task description can be realized with Xtext.

5.1.1 Extending the Java Validator class

With the first translation of the Xtext grammar, the generator has already created the necessary infrastructure to implement custom validation rules. Look into the package org.eclipse.xtext.example.ql.validation

, you will find 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 integration, our Xtend based validator class will be inserted into the class hierarchy of

QlDslJavaValidator

.

Create an Xtend class QlDslXtendValidator, and extend it from AbstractQlDslJavaValidator.

5

6

7

8

9

10

11

12

3

4

1

2 package org.eclipse.xtext.example.ql.validation

import javax.inject.Inject

import org.eclipse.xtext.validation.Check

import org.eclipse.xtext.xbase.XFeatureCall

import org.eclipse.xtext.xbase.XbasePackage

import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations

import static extension org.eclipse.xtext.nodemodel.util.NodeModelUtils.* class QlDslXtendValidator extends AbstractQlDslJavaValidator {

@Inject extension IJvmModelAssociations

}

40 http://www.eclipse.org/modeling/emf/?project=validation

41 see http://www.eclipse.org/Xtext/documentation.html#validation

69

Derive QlDslJavaValidator from the the Xtend validator:

1

2

3 public class QlDslJavaValidator extends QlDslXtendValidator {

// do nothing here, rules are implemented in Xtend

}

LWC13 Submission

5.1.2 Constraint: Ensure order of questions

The first constraint in the LWC13 task is defined so:

Test for cyclic dependencies. For instance, the following snippet should be rejected:

1

2 if (x) { y: "Y?" boolean } if (y) { x: "X?" boolean }

The reason is that y will only be asked for when x is true, but x will 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 offset 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

.

1

2

3

4

5

6

7

8

9

10

11

12

Now open the QlDslXtendValidator class again and add this method

42

:

@Check def void check_featureDeclaredBeforeCall (XFeatureCall featureCall) { val featureSource = featureCall.feature.sourceElements.head

val nodeFeature = if (featureSource != null ) featureSource.node

else featureCall.feature

.node

val nodeCall = featureCall.node

if (nodeFeature != null ) { if (nodeFeature.offset > nodeCall.offset) { error(featureCall.feature.simpleName+ " must be declared before." ,featureCall,

XbasePackage::eINSTANCE.XAbstractFeatureCall_Feature, "

ERR_FEATURE_CALL_BEFORE_DECLARATION" , null )

}

}

}

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 Xbase’s 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.

17

18

19

20

14

15

16

9

10

11

12

13

21

22

23

24

1

2

3

4

7

8

5

6 package org.eclipse.xtext.example.ql.validation.test

import javax.inject.Inject

import org.eclipse.xtext.example.ql.QlDslInjectorProvider

import org.eclipse.xtext.example.ql.qlDsl.Questionnaire

import org.eclipse.xtext.junit4.InjectWith

import org.eclipse.xtext.junit4.XtextRunner

import org.eclipse.xtext.junit4.util.ParseHelper

import org.eclipse.xtext.junit4.validation.ValidationTestHelper

import org.eclipse.xtext.xbase.XbasePackage

import org.junit.Before

import org.junit.Test

import org.junit.runner.RunWith

@RunWith( typeof (XtextRunner))

@Inject With( typeof (QlDslInjectorProvider)) class QlDslValidationTest {

@Inject extension ParseHelper<Questionnaire> parseHelper

@Inject extension ValidationTestHelper

@Before def void setUp () {

} parseHelper.fileExtension= "ql"

72

LWC13 Submission

29

30

31

32

33

25

26

27

28

38

39

40

41

42

43

44

45

46

47

34

35

36

37

52

53

54

55

56

49

50

51

60

61

62

63

57

58

59

64

65

66

@Test def void testValidation_CallBeforeDeclaration_expectError () {

’’’ form Foo { if (x) { y: "Y?" boolean } if (y) { x: "X?" boolean }

}

’’’ .parse.assertError(XbasePackage::eINSTANCE.XFeatureCall, "

ERR_FEATURE_CALL_BEFORE_DECLARATION" , "must be declared before" )

}

@Test def void testValidation_CallBeforeDeclaration_expectSuccess () {

’’’

} form Foo { x: "foo" boolean if (x) { a: "X?" boolean }

}

’’’ .parse.assertNoErrors

48

// 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.

@Test def void testValidation_ConditionTypeCheck_expectError () {

’’’ form Foo { if ("foo".length) { a: "X?" boolean }

}

’’’ .parse.assertError(XbasePackage::eINSTANCE.XMemberFeatureCall,

"org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types" , "Type mismatch" )

}

@Test def void testValidation_ConditionTypeCheck_expectSuccess () {

}

’’’ form Foo {

} if ("foo".length>1) { a: "X?" boolean }

’’’ .parse.assertNoErrors

73

LWC13 Submission

71

72

73

74

75

76

77

67

68

69

70

81

82

83

84

78

79

80

85

86

}

@Test def void testValidation_AssignmentTypeCheck_expectFailure () {

’’’ form Foo { a: "X?" boolean ("foo".length)

}

’’’ .parse.assertError(XbasePackage::eINSTANCE.XMemberFeatureCall,

"org.eclipse.xtext.xbase.validation.IssueCodes.incompatible_types" , "Type mismatch" )

}

@Test def void testValidation_AssignmentTypeCheck_expectSuccess () {

’’’ form Foo { a: "X?" boolean ("foo".length>1)

}

’’’ .parse.assertNoErrors

}

Basically, the class is a plain JUnit 4 class. We have to use a special JUnit execution class,

XtextRunner

, and provide a language specific initializer class, QlDslInjectorProvider.

5

6

7

3

4

1

2

Next, the class adds two extension classes. ParseHelper provides a parse() method for char sequences. This will parse and validate the model. Afterwards the observed issues can be asserted with the methods from the

@RunWith( typeof (XtextRunner))

@Inject With( typeof (QlDslInjectorProvider)) class QlDslValidationTest {

@Inject extension ParseHelper<Questionnaire> parseHelper

@Inject extension ValidationTestHelper

...

}

4

5

6

7

1

2

3

Now the test methods can be implemented. They are super simple:

@Test def void testValidation_CallBeforeDeclaration_expectSuccess () {

’’’ form Foo { x: "foo" boolean if (x) { a: "X?" boolean }

}

74

LWC13 Submission

8

9 }

’’’ .parse.assertNoErrors

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 files (usually pom.xml). For the LWC example we have added such POM files 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 Workflow plugin is used to execute MWE workflows 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 configurations must be contained in a profile. We define a profile external-repositories

, which is activated by default in the activeProfiles section.

13

14

15

16

17

8

9

10

11

12

6

7

4

5

1

2

3

27

28

29

30

24

25

26

31

32

33

18

19

20

21

22

23

<settings>

<activeProfiles>

<activeProfile>external-repositories</activeProfile>

</activeProfiles>

<profiles>

<profile>

<id>external-repositories</id>

<repositories>

<repository>

<id>Eclipse Juno</id>

<layout>p2</layout>

<url>http://download.eclipse.org/releases/juno</url>

</repository>

<repository>

<id>Eclipse Orbit</id>

<layout>p2</layout>

<url>http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/ repository/

</url>

</repository>

<repository>

<id>Eclipse Xtext</id>

<layout>p2</layout>

<url>http://download.eclipse.org/modeling/tmf/xtext/updates/composite/releases/

</url>

</repository>

</repositories>

<pluginRepositories>

<pluginRepository>

<id>fornax.releases</id>

<name>Fornax Release Repository</name>

<url>http://www.fornax-platform.org/nexus/content/repositories/releases/

</url>

</pluginRepository>

76

LWC13 Submission

38

39

40

41

34

35

36

37

<pluginRepository>

<id>xtend</id>

<url>http://build.eclipse.org/common/xtend/maven/</url>

</pluginRepository>

</pluginRepositories>

</profile>

</profiles>

</settings>

The settings file is placed in the repository under /devenv/lwc13.devenv/settings.xml.

In order to use this settings file, 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 fil is placed in the root of

the repository

43

.

The Parent POM is responsible for different things:

1

2

• Definition of the common group identifier and version

<groupId>org.eclipse.xtext.example.ql</groupId>

< version >1.0.0-SNAPSHOT</ version >

9

10

6

7

8

1

2

3

4

5

• Tycho Plugin configuration. Note the <extensions>true</extensions> entry. To make the Tycho version that is used easier configurable, a property tycho-version is defined, which is used for all Tycho plugin configurations.

<properties>

<tychoversion >0.17.0</tychoversion >

</properties>

<build>

<plugins>

<plugin>

<groupId>org.eclipse.tycho</groupId>

<artifactId>tycho-maven-plugin</artifactId>

< version >${tycho-version}</ version >

<extensions>true</extensions>

43 http://code.google.com/a/eclipselabs.org/p/lwc13-xtext/source/browse/pom.xml

77

LWC13 Submission

11

12

</plugin>

...

10

11

7

8

9

5

6

3

4

1

2

15

16

17

18

12

13

14

19

20

21

22

23

24

25

26

• Supported Target Environments. Eclipse is partially OS dependent. Which platforms should be considered in the target platform configuration is configured with the targetplatform-configuration plugin.

<plugin>

<groupId>org.eclipse.tycho</groupId>

<artifactId>target-platform-configuration</artifactId>

< version >${tycho-version}</ version >

<configuration>

<resolver>p2</resolver>

<pomDependencies>consider</pomDependencies>

<environments>

<environment>

<os>win32</os>

<ws>win32</ws>

<arch>x86</arch>

</environment>

<environment>

<os>win32</os>

<ws>win32</ws>

<arch>x86_64</arch>

</environment>

<environment>

<os>macosx</os>

<ws>cocoa</ws>

<arch>x86_64</arch>

</environment>

</environments>

</configuration>

</plugin>

5

6

7

8

3

4

1

2

• Java Source Code version. The produced code requires Java 1.6 for compilation. This needs to be configured at the tycho-compiler-plugin.

<plugin>

<groupId>org.eclipse.tycho</groupId>

<artifactId>tycho-compiler-plugin</artifactId>

< version >${tycho-version}</ version >

<configuration>

<encoding>UTF-8</encoding>

<meminitial>128m</meminitial>

<maxmem>1024m</maxmem>

78

LWC13 Submission

9

10

11

12

13

<source>6.0</source>

<target>6.0</target>

<verbose>true</verbose>

</configuration>

</plugin>

10

11

7

8

9

5

6

3

4

1

2

• Maven Plugin Management. In the pluginManagement section the plugins that potentially participate in the build are configured 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 fix the versions of plugins that are used to those with which the build has been tested. Also basic plugin configurations can be added here.

<plugin>

<groupId>org.eclipse.tycho</groupId>

<artifactId>tycho-p2-repository-plugin</artifactId>

< version >${tycho-version}</ version >

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-clean-plugin</artifactId>

< version >2.4.1</ version >

</plugin>

...

5.2.3 Reactor POM

1

2

3

4

7

8

5

6

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

:

<modules>

<module>org.eclipse.xtext.example.ql</module>

<module>org.eclipse.xtext.example.ql.ui</module>

<module>org.eclipse.xtext.example.qls</module>

<module>org.eclipse.xtext.example.qls.ui</module>

<module>org.eclipse.xtext.example.ql.sdk</module>

<module>org.eclipse.xtext.example.ql.repository</module>

</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

Let’s look a bit at the POM of the QL Runtime Project org.eclipse.xtext.example.ql.

3

4

5

6

7

8

9

10

11

12

1

2

<?

xml version = "1.0" encoding= "UTF-8" ?>

<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" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" >

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.eclipse.xtext.example.ql</groupId>

<artifactId>parent</artifactId>

< version >1.0.0-SNAPSHOT</ version >

<relativePath>../../pom.xml</relativePath>

</parent>

<artifactId>org.eclipse.xtext.example.ql</artifactId>

...

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 parent’s 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 qualifier. In our case here, the project version is 1.0.0-SNAPSHOT, which means the bundle version must be 1.0.0.qualifier. The qualifier gets replaced at build time by a timestamp.

1

2

Bundle-Name: org.eclipse.xtext.example.ql

Bundle-Version: 1.0.0.qualifier

1

The next entry, packaging, is especially important.

<packaging>eclipse-plugin</packaging>

80

LWC13 Submission

1

2

3

4

5

6

7

8

12

13

14

15

16

9

10

11

20

21

22

23

17

18

19

29

30

31

32

24

25

26

27

28

Tycho introduces some Eclipse specific packaging types, which trigger the module to be built by Tycho plugins.

<build>

<resources>

<resource>

<directory>${project.build.directory}/xtext</directory>

</resource>

</resources>

<plugins>

<!

-- Copy all Xtext related sources to seperate folder that is registered as resource folder ->

<plugin>

<artifactId>maven-resources-plugin</artifactId>

<executions>

<execution>

<id>copy-resources</id>

<phase>initialize</phase>

<goals>

<goal>copy-resources</goal>

</goals>

<configuration>

<outputDirectory>${project.build.directory}/xtext</outputDirectory>

<resources>

<resource>

<directory>src</directory>

<includes>

<include>**/*.xtext</include>

<include>**/*.mwe2</include>

</includes>

</resource>

</resources>

</configuration>

</execution>

</executions>

</plugin>

1

2

Next, we do something special for MWE2. In order to execute an MWE2 workflow this workflow file must be loadable from the project’s classpath. But the MWE2 file 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.

<plugin>

<artifactId>maven-clean-plugin</artifactId>

81

LWC13 Submission

17

18

19

20

14

15

16

21

22

23

24

25

29

30

31

26

27

28

9

10

11

12

13

7

8

5

6

3

4

<configuration>

<filesets>

<fileset>

<directory>src-gen</directory>

<excludes>

<exclude>.gitignore</exclude>

</excludes>

</fileset>

<fileset>

<directory>xtend-gen</directory>

<excludes>

<exclude>.gitignore</exclude>

</excludes>

</fileset>

<fileset>

<directory>../${project.artifactId}.ui/src-gen</directory>

<excludes>

<exclude>.gitignore</exclude>

</excludes>

</fileset>

<fileset>

<directory>../../tests/${project.artifactId}.tests/src-gen</directory>

<excludes>

<exclude>.gitignore</exclude>

</excludes>

</fileset>

</filesets>

</configuration>

</plugin>

When the workflow 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 configure the maven-clean-plugin to clean up sources also from the dependend projects. Normally we would need to configure 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

22

23

24

25

26

27

12

13

14

15

16

10

11

8

9

17

18

19

20

21

3

4

1

2

5

6

7 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.

<plugin>

<groupId>org.fornax.toolsupport</groupId>

<artifactId>fornax-oaw-m2-plugin</artifactId>

<executions>

<execution>

<id>xtext</id>

<phase>generate-sources</phase>

<goals>

<goal>run-workflow</goal>

</goals>

<configuration>

<workflowEngine>mwe2</workflowEngine>

<workflowDescriptor>org.eclipse.xtext.example.ql.GenerateQlDsl</workflowDescriptor>

<timestampFileName>xtext-generator.timestamp</timestampFileName>

<jvmSettings>

<fork>true</fork>

<jvmArgs>

<jvmArg>-Xms100m</jvmArg>

<jvmArg>-Xmx700m</jvmArg>

<jvmArg>-XX:MaxPermSize=128m</jvmArg>

<jvmArg>-Dlog4j.configuration=file:${basedir}/META-INF/log4j.properties</jvmArg

>

</jvmArgs>

</jvmSettings>

</configuration>

</execution>

</executions>

</plugin>

The fornax-oaw-m2-plugin plugin is responsible to invoke the MWE2 workflow GenerateQlDsl.mwe2.

The module value in this workflow is configured as parameter workflowDescriptor to this plugin.

The plugin is executed in the generate-sources lifecycle phase, which is processed before compilation. You will see this output when it is executed:

1

2

3

4

[INFO] --- fornax-oaw-m2-plugin:3.4.0:run-workflow (xtext) @ org.eclipse.xtext.example.ql

---

[INFO] Fornax Model Workflow Maven2 Plugin V3.4.0

[INFO] Executing workflow in forked mode.

[INFO] Workflow ’org.eclipse.xtext.example.qls.GenerateQlsDsl’ finished.

83

LWC13 Submission

1

2

3

4

Last but not least the xtend-maven-plugin is configured:

<plugin>

<groupId>org.eclipse.xtend</groupId>

<artifactId>xtend-maven-plugin</artifactId>

</plugin>

This plugin compiles the Xtend files to Java files. It does some fancy stuff, since Xtend files 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 files 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 configured.

10

11

7

8

9

12

13

14

5

6

3

4

1

2

<?

xml version = "1.0" encoding= "UTF-8" ?>

<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" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" >

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.eclipse.xtext.example.ql</groupId>

<artifactId>parent</artifactId>

< version >1.0.0-SNAPSHOT</ version >

<relativePath>../../pom.xml</relativePath>

</parent>

<artifactId>org.eclipse.xtext.example.ql.ui</artifactId>

<packaging>eclipse-plugin</packaging>

</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 configured further.

9

10

6

7

8

3

4

5

11

12

13

14

1

2

<?

xml version = "1.0" encoding= "UTF-8" ?>

<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" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" >

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.eclipse.xtext.example.ql</groupId>

<artifactId>parent</artifactId>

< version >1.0.0-SNAPSHOT</ version ><!

--X->

<relativePath>../../pom.xml</relativePath>

</parent>

<artifactId>org.eclipse.xtext.example.ql.sdk</artifactId>

<packaging>eclipse-feature</packaging>

</project>

5.2.7 p2 Repository

The result of the build should be a p2 repository, which the user can configure as update site.

The repository project org.eclipse.xtext.example.ql.repository

45

contains a category.xml

file, which references the feature that should be available on this repository:

4

5

6

7

1

2

3

<?

xml version = "1.0" encoding= "UTF-8" ?>

<site>

<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" >

<category name= "DSL" />

</feature>

<category-def name= "DSL" label= "DSL" />

</site>

The POM for this project has the packaging type eclipse-repository, besides that it is as simple as the previous POM.

3

4

1

2

<?

xml version = "1.0" encoding= "UTF-8" ?>

<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" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" >

<modelVersion>4.0.0</modelVersion>

45 http://tinyurl.com/cho28ox

85

LWC13 -

9

10

11

12

13

14

7

8

5

6

<parent>

<groupId>org.eclipse.xtext.example.ql</groupId>

<artifactId>parent</artifactId>

< version >1.0.0-SNAPSHOT</ version ><!

--X->

<relativePath>../../pom.xml</relativePath>

</parent>

<artifactId>org.eclipse.xtext.example.ql.repository</artifactId>

<packaging>eclipse-repository</packaging>

</project>

The resulting repository is then available in the target/repository folder.

Submission

5.2.8 Continuous Integration

For continuous integration we use the build infrastructure from Cloudbees, which is free for open-source software. Cloudbees offers 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 configuration 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

• Configure POM: projects/pom.xml

• Goals and options: clean install

• Alternative settings file (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 first-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 fit 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 flexible frameworks we know of. This flexibility 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 different 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 effort and we would be pleased to hear from you!

Karsten Thoms, Johannes Dicks, Thomas Kutz

April 2013

88

Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project