Have you heard about model driven software development (MDD / MDSD) and are thinking “what’s all this fuzz about models”? “Why should models help me to be more productive,” might be another thought you have.
People have been asking how to leverage models on and off on the web and in meetings I attended, so I thought I might share this little tutorial with you. In this tutorial, we will develop a little code generator that helps you to create (HTML) forms from models.
A short Overview
Xpand is a template engine, similar to FreeMarker, Velocity, JET and JSP. However, it features some very unique properties that makes using Xpand very well suited for generating code from models, such as type safety and polymorphic dispatch. If you haven’t heard those terms before, fear not! I’ll show you how to use Xpand by way of an easy-to-follow example.
The usual process of writing a code generator with Xpand is as follows:
Define the structure of the model you want to process. This is called a metamodel
Define one or more template(s) that teach the code generator how to translate your model into code.
Easy, isn’t it?
Using the code generator is even easier:
Create a model
Start the code generator
It is worth mentioning that you can use Xpand to generate code for almost any known programming language. Everything you can express in text can be generated using Xpand. So, while we will be generating HTML and Java code in the following example, you can easily write code templates that generate code for C#, Basic, Lua, SmallTalk, ABAP, or any other programming language. You can also generate manuals and other documentation artifacts from your models using Xpand. I’ve successfully used Xpand to create DocBook files from models. Those DocBook files have then been converted to PDF files and online help files.
Preparing your IDE
Grab and install a recent copy of Eclipse. At the time of writing, I am using Eclipse 3.6 M6.
Install the latest version of Xpand (Help –> Install New Software …)
Add the Xpand update site (at the time of this writing, I am using Xpand 1.0 nightly builds from http://download.eclipse.org/modeling/m2t/xpand/updates/nightly/)
Add the MWE update site (http://download.eclipse.org/modeling/emft/mwe/updates/nightly/)
Select MWE and Xpand:
- After the obligatory restart, do yourself the favour and set the platform encoding to UTF-8!
Creating a Generator Project
Xpand code generators are hosted in Eclipse plug-ins, mainly because this makes handling the classpath a lot easier. People have reportedly used Maven to run Xpand code generators, but we won’t go down this road today. Let’s create a simple generator project:
Open the new project wizard and choose Xpand Project from the list
Choose a meaningful name for your project (e.g. org.xpand.example.gettingstarted
Select Create a sample EMF based Xpand project
After clicking Finish, the wizard will create an sample generator project for you:
Before we can start working with this project, we need to perform some clean-up actions:
Open src/metamodel/Checks.chk and delete all its contents
Open src/metamodel/Extensions.chk and delete all its contents
Open src/template/GeneratorExtensions.ext and delete all its contents
Don’t forget to save all modified files
Creating the Metamodel
As mentioned before, we need to define the structure of our models before we can actually start writing the code template. Xpand is capable of understanding a variety of metamodel types. For example, if you have an XML schema file, you can use this as a metamodel and thereby enable Xpand to use XML files which are compliant to your schema as input models. Or, if you already have a bunch of Java files making up your data model, you can use those to drive Xpand code generation. In this tutorial, however, we will be using an Ecore metamodel to define the structure of our models. The project has already been configured to support Ecore metamodels, so all we need to do is open metamodel.ecore and define the structure:
Please open src/metamodel/metamodel.ecore .
Remove the following elements from the metamodel: Feature, Entity, Datatype, Type, Model. The metamodel should now be empty:
Select package metamodel (as depicted in the screenshot above) and add a new EClass (context menu –> New Child –> EClass). Use the properties view to change the name of the newly created EClass to Model.
Create a new EClass Form
Select the newly created EClass Form and add the following _EAttribute_s:
name, set the EType to EString
description, EType = EString
title, EType = EString
Select EClass Model and add a new EReference, setting its attributes as follows:
name = forms
EType = Form
Containment = true
Upperbound = -1 (meaning: unlimited)
Create a new EClass Field and add the following EAttributes to it:
name, EType = EString
label, EType = Estring
Create another EClass TextField, setting its properties as follows:
name = TextField
ESuper Types = Field
Add one EAttribute text to Textfield:
- name = text, EType = EString
. Add an EClass MultiLineTextField to the metamodel:
name = MultiLineTextTield
ESuper Types = TextField
. Now that we have everything in place, we finally need to add a reference from Form to Field so we can later add fields to a form. Select EClass Form and add an EReference to it, setting its properties as follows:
name = fields
EType = Field
Containment = true
Upperbound = -1 (meaning: unlimited)
Creating a Model
Let’s now create a model that follows the structure of the metamodel:
In metamodel.ecore, select EClass Model
Create a model instance by choosing Create Dynamic Instance… from the context menu
Save the model file to src/Model.xmi
The model file editor will now open and you can use the tree editor to input the following model:
Add a Form to the model, seeting the following properties:
Description: Send your feedback
Title: Contact form
Add a TextField to the Form, setting the following properties:
Add another TextField to the Form, setting the following properties:
Add a MultiLineTextField to the Form, setting the following properties:
Your model should now look like this:
Creating a Code Generator
As mentioned before, we will create a code generator for simple forms. Nothing too fancy, but enough to give you an idea of how to create generator templates.
The result will look like this:
Open src/template/Template.xpt and replace its contents with the following text:
«IMPORT metamodel» «DEFINE main FOR Model» «EXPAND form FOREACH forms» «ENDDEFINE»
On the first line, we import the metamodel so that the generator (and the editor as well) knows about the structure of our model. On line 2 – 4 we define a code template named main, making sure it is bound to model elements of type Model. The template doesn’t do much, except to call another template (which we will define in a minute) named form with the collection of Form_s, contained in the reference forms_ of the current form.
Add the following lines to the template file, defining the template for the HTML file:
«DEFINE form FOR Form» «FILE name + ".html"» <!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"> <head> <title>«this.title»</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="../static/style.css" /> </head>
In the first line, we start the template by specifying its name (form) and the type it is bound to (Form). On the next line, we use the FILE statement to specify the file the output is going to be written to. The name of the file is derived by concatenating the attribute name of the current Form and the string literal “.html”.
Continue the template by appending the following text:
<body> <div id="page-wrap"> <h1>«this.title»</h1><br /><br /> <p>«this.description»</p> <div id="form-area">
Obviously, this piece of template code will create part of the body of the HTML page. Again, we will access attributes of the current Form (as read from the model) and insert their values into the template (in this case, the title and the description). By the way, you can omit the this. prefix in front of the variable names.
The template goes on with the following text:
<form method="post" action="form.php"> «EXPAND field FOREACH this.fields» <input type="submit" name="submit" value="Submit" class="submit-button" /> </form> <div style="clear: both;"></div> </div> </div> </body> </html> «ENDFILE» «ENDDEFINE»
The EXPAND statement will invoke yet another subtemplate with the name field. This sub template will be called for each element in the fields attribute (reference) of the current Form.
You might recall that we we defined three different kinds of _>Field_s in the metamodel:
Field (which is the super class for the other two field types
As their names imply, a TextField will be a single line text entry field, whereas MultiLineTextField will be a multiline text input field. We somehow need to be able to render different HTMNL code for each of these different text field types.
As mentioned in the introduction, Xpand is not only type safe, but also supports polymorphic dispatch. This basically means we will create three templates (one for each of the different field types) with the same name. When evaluating the code template, the Xpand generator will dispatch to the appropriate template by matching the most concrete type of the current model element.
Add the following code to the template file:
«DEFINE field FOR Field» «ERROR "should not happen"» «ENDDEFINE» «DEFINE field FOR TextField» <label for="«this.name»">«this.label»:</label> <input type="text" name="«this.name»" id="«this.name»" /> «ENDDEFINE» «DEFINE field FOR MultiLineTextField» <label for="«this.name»">«this.label»:</label> <textarea name="«this.name»" id="«this.name»" rows="20" cols="20"></textarea> «ENDDEFINE»
As you can see, all three templates have the same name. Only the type they are bound to differs. This is enough to let Xpand know which template to choose according to the type of the current model element. Let’s suppose Xpand is iterating the model and the current model element is a TextField. Although Field is a direct super type of TextField, Xpand will not invoke the first template («DEFINE field FOR Field»), but the second template («DEFINE field FOR TextField»), as this is the most concrete match for the type of the model element.
Running the Code Generator
If you have followed the above steps, running the code generator is a piece of cake:
Open the context menu on src/workflow/workflow.mwe and select Run As –> MWE Workflow
This will start the code generator. You will see some log messages in the console view. If all went well, the output in the console reads something like this:
... 1503 INFO Generator - Written 1 files to outlet [default](src-gen) 1503 INFO WorkflowRunner - workflow completed in 650ms!
The result of the code generation can be found in src-gen/contact.html. As this file has some dependencies to CSS/image files, please download the files from the static folder (here) and place them in your project before opening contact.html in your browser.
Where to go from here
If you want to learn more about Xpand, be sure to attend my talk Use models and let the computer do the grunt work with Xpand at EclipseCon 2010. I’ll show some more advanced topics in this talk (such as generator cartridges, using Xtend to augment your models, partitioning your code templates, using other metamodels to define your models).
Xpand comes with an extensive documentation (just go to Help –> Help Contents –> Xpand Documentation in Eclipse). You can also get help on Xpand in the Eclipse Community Forums
Should you need help, itemis (the company I work with) offers training and consulting for Xpand and a host of other modeling related technologies.
No doubt you have heard about Xtext. Xpand and Xtext go together great: you can use Xtext to define the structure of your models and create great-looking text editors to edit your models. Then, use Xpand to create code generators that take your textual models and turn them into running software. Actually, Xtext comes with a wizard that you to create a code generator project for your DSL.
The code for this tutorial can be found in my SVN repository on Google Code.