The Acceleo editor is by default associated with the .mtl file extension.
The editor uses specific colors for Acceleo templates:
- red is used for template tags;
- purple is used for other tags (queries, modules, imports, ...);
- blue is used for dynamic expressions in templates or other places;
- green is used for comments and String literals;
- black is used for static text or query bodies.
The content assistant is traditionally invoked with Ctrl + space.
We have already met it severral times in this guide.
It proposes a choice of all elements that make sense at the place of invocation.
It is available everywhere, so don't hesitate to hit Ctrl + space anywhere!
Example of content assistant on a type:
On a metamodel:
Content assistant is also very useful in a multitude of situations. We will
give some examples, simply remember to hit Ctrl + Space whenever you want, it
will probably help you a lot!
Here is a view of all options you get when hitting Ctrl + Space in a template:
And here is the options proposed out of a template, when hitting Ctrl + Space
directly in an Acceleo module file:
Templates and comments can be folded thanks to a marker located in the left
margin in front of each of these elements.
Here is a very useful trick in Acceleo. Rapid text replacement allows you to
quickly replace all occurrence of a chosen piece of text by some template
invocation. Case differences are inferred generating toUpper() or
toUpperFirst() depending on what's needed.
Here is an example:
In a classical bottom-up approach, you have written your code first, and you now
implement the Acceleo template from this code.
What you want to do here is to replace all occurrences of "att1" by a dynamic
behavior, i.e. by a template call.
The easiest way to do this is to select one of the "att1" in the editor, and
invoke the content assistant by hitting Ctrl + Space.
The completion assistant proposes to replace all occurrences of the selected
text by a template call. A preview of the result is displayed in a tooltip close
to the completion window.
When you accept this option, all occurrences are replaced and you can
immediately enter the template invocation needed, which is simultaneously
replaced in all relevant locations.
All occurrences of att1 have been replaced by [javaName()/] (because
javaName() is what was entered manually), but Att1 has been replaced by
[javaName().toUpperFirst()/].
The next thing you'd want to do in the above example is to replace all
occurrences of int by something like [javaType()/], and implement the
javaType template to write the java type of the class attributes.
Just proceed the same way and you're done. No risk to forget any occurrence of
your type anymore!
You will probably not use this feature every day, but it is important to know
that it exists because it can bring you comfort from time to time.
For example, you might have multiple widget types : Text, Spinner, Button,...
and, starting from an example, want to customize the generation module for each
of these widgets.
The example below shows a sample of a place where we want to implement a
behavior for each widget:
Once the action executed, we end up with this:
Of course, this means we still need to change the conditions of these if
statements; but it simplifies the transformation of the template into what we
need:
Acceleo provides a few specific views to improve productivity when working with
templates.
These views will be detailed later on, in the relevant chapters.
Let's just summarize their purpose to get the big picture.
As usual, they can be opened by clicking on Window > Show View and selecting
the appropriate view in the menu. Note that in the Acceleo perspective, Acceleo
views are proposed by default directly in this menu, which will not be the case
in another perspective where you'll have to find them in the Other... popup.
Generation patterns have been introduced in acceleo because we noticed that
something that happens really often when developing code generators is the need
to implement some behavior on an interface and all or part of its subtypes.
For example, let's imagine you are implementing a java generator from UML.
What you want is to have a template called javaName which will generate the
name of any classifier, with some default behavior and some specific behavior on
classes and interfaces.
This is where the Generation Patterns view comes into play:
- Locate the cursor in the template, at the position where you want to insert
your javaName templates;
- In the Generation patterns view, select "[template] for all selected types" in
the top part
- Select the types for which you want to create javaName templates for.
Note the bottom part of the Generation Patterns views presents a
hierarchical view of the metamodel you are using. Each node represents a type of
the metamodel and contains all of its subtypes. So, one type can appear several
times in this view, one time for each of its super-classes or super-interfaces.
When you select a node (by checking the combo-box before it), all its
descendants are also selected by default, but you can uncheck those you don't
need.
Once you have selected the types you need, go back to the editor and activate
the completion by hitting Ctrl+Space. The first choice should be
"[template] for all selected types", select it.
New templates are then inserted into you Acceleo module. They are called "name"
by default but you can immediately rename them by just entering the name you
want. All templates will be renamed simultaneously.
Here, Acceleo has done his job, now it's time for you to do yours: implement
these newly created templates!
This view presents you with every Acceleo element that is accessible in your
workbench (no matter your current project's dependencies). You can select
one or several elements (use the checkboxes) and override them.
Note: If the meaning of "override" is not clear, you may want to refer to
the official MTL Specification.
Templates displayed in this view can be anywhere in your workspace or in your
plug-ins.
So, this view can be used for:
- Selecting templates you want to override (which is its main purpose);
- Navigating to templates in your plug-ins to see their implementation without
having to explicitly import their plug-in(s) in your workspace.
To override one or several existing templates, just select them in this view by
checking their checkboxes. Then, edit the module in which you will override the
templates, place the cursor where you want to insert the overriding templates,
and hit Ctrl + Space.
Select the first choice ("Selected Overrides") and hit Return.
The overriding templates are then created. Note that by default, their
implementation is initialized with their original implementation.
Note: A marker indicates whether a given project is accessible from yours.
If this is the case, a green mark indicates everything is fine.
Otherwise, a red marker indicates that you need to import the project in yours
to be able to override a template it contains.
For example, in the screenshot below,
org.eclipse.acceleo.module.other.sample needs be imported in your current
project before you can successfully override one of its templates.
Nevertheless, you can declare the overriding, it will just not compile
while you have not imported the relevant project (which is done in the
MANIFEST.MF file of your Acceleo project).
Acceleo allows static overriding as described in the MTL specification.
Acceleo also allows another kind of overriding, which is called "dynamic".
Dynamic overriding allows you to override any template called by a given module
even if the launcher of this module knows nothing about your project.
It takes precedence over any static template overriding.
With dynamic overriding, you can make sure a specific template will be called
while calling the original generator (the initial java class that launches the
generation).
Dynamic overriding only works "out-of-the-box" inside of eclipse.
To activate dynamic overriding, you must place the overriding template on a
plug-in which will extend the org.eclipse.acceleo.engine.dynamic.templates
extension point.
To do that, open the META-INF/MANIFEST.MF file of your plug-in, go to the
Extensions tab, and click on the "Add..." button.
Click on "Finish".
The extension point requires only one piece of information, which is the path to
a module file or folder.
If it is a folder, Acceleo looks for modules recursively and takes them all into
account for dynamic overriding.
You can use the "Browse..." button to select the file or folder.
Note: Your plug-in must be a singleton to declare an extension.
This view displays the results of the latest generation run.
It displays the list of projects where some code has been generated.
In each project, the files that have been generated (in their folders).
For each file, the view displays:
- A list of model elements used for their generation, in a hierarchical way;
- A list of Acceleo modules used for their generation.
You can double-click on any element to visualize the related portions of
generated text.
You can right-click on any element and select Open Declaration to navigate
to any atomic element used during the generation, be it a model element or an
Acceleo element (template, query).
This view's behavior will be further detailed in chapter
Using the Result View.
One of the great benefits of modern IDE tooling is the capacity to easily
navigate in code from elements to their declarations and, vice-versa, from
declarations to usages.
These features are available in Acceleo.
The traditional shortcut F3 is supported by Acceleo, along with Ctrl +
click, which both take you to the declaration of the selected or clicked
element.
This is supported for all kinds of elements: templates, queries, metamodels,
metamodel elements, EOperations, etc.
This can also be achieved by right-clicking on an element, then Open
Declaration.
Conversely, it is possible to get all the elements that refer to a given element.
The shortcut is Ctrl + Shift + G, but it can also be achieved by right-clicking
on the element then Search References.
The relevant elements are displayed in the Search view.
Obviously, Acceleo displays error markers when errors are detected.
Error markers also appear in the eclipse Problems view, as usual. Files with
errors also appear with an error decorator.
Just hover the marker in the editor margin with the mouse to get a tooltip to
appear with an explanation of the problem.
Acceleo displays error markers whenever a module file cannot be compiled,
whatever the reason. But more, Acceleo also displays error markers when it finds
inconsistencies between a module and other elements, such as the containing
plug-in's MANIFEST.MF file.
For instance, if a module's main file is located in a package which is not
exported by its plug-in, an error marker is added because the main file cannot
be run if the plug-in does not export its package.
Errors appear in the "Problems" view (generally displayed at the bottom of the
perspective), and double-clicking on an error in this view directly takes you
to the file where it is located.
In the example above, the [javaName()] tag is never closed. Just replace it
with [javaName()/] (notice the slash to close the tag) and the error
disappears.
Quick fixes are available with the shortcut Ctrl + 1.
Currently, quick fixes propose to create a supposedly missing template or query,
before or after the current template.
In the following example, we just write the call to a template that does not
exist yet, and use the quick fix to create it immediately.
Another quick fix available creates a new query that wraps a java service, as
described in the Wrapping Java Services section.
Imagine you have java methods called service1, service2, service3
(which of course are not recommended names!) in a class that you can access
from your Acceleo project (it is either directly in your project, or imported).
Enter service in your template and save it.
A red marker appears since it does not compile.
Hit Ctrl + 1, and select Create Java service wrapper.
Acceleo looks for a method starting by "service" in the accessible classes and
creates queries for each of them, inserting them at the end of your module file.
Note: More quick fixes will be provided in the next versions of Acceleo.
The renaming functionality is accessible via Alt + Shift + R, as usual in
eclipse.
This allows templates and variables to be renamed in a coherent manner: All
references to the renamed element are updated to use the new name, as expected.
Note that when selecting an element in the editor, all the occurrences of the
same element are highlighted, which makes it very easy to find where a given
template is being used.
When hitting Alt + Shift + R, a window appears where the new name must be
entered.
Names already in use are forbidden.
From here, it is possible to preview the changes that will be made by clicking
on the Preview > button, or to make the changes immediately by clicking on
OK.
The preview displays the files that will be modified and for each of them the
changes that are to be applied to their content.
The left side of the preview displays the current state of the module, and the
right side displays the future state of the module after the renaming takes
place.
It is sometimes useful to invoke some java code from inside an Acceleo template.
The acceleo non-standard library provides a service invoke which allows just
that. The invoked java service can be wrapped in an Acceleo query.
To facilitate the creation of such a wrapper, proceed like this:
Right-click on the package you want to create your Acceleo module in, and
select New > Acceleo Module File
In the wizard, enter the relevant information in the usual fields, then click
on the Advanced >> button
Check the Initialize Content checkbox
Select Create a Java services wrapper in the listbox below
Select the java file that contains the services to wrap
Click on the Finish button
That's it!
An Acceleo module is created, with a query for each service found in the
original java class.
It is often useful (actually, it is recommended) to use a bottom-up approach
to develop Acceleo templates.
So, before beginning to write templates, start by prototyping your target files,
make sure they work as expected, and then you are ready to start generating them.
An existing application can be used as a starting point to create an Acceleo
module.
By the way, it is a good idea to mimick the target's organization in the Acceleo
module's organization: One generation module per kind of file to generate, each
located in a package named after the target package's name.
At that time, you'd like to import the content of some files into a new template.
Let's imagine you have written the following class sample in your bottom-up
approach. (Of course, this is a stupid example, you'll have to work a bit more
for this approach to prove useful!)
Right-click on the package you want to create your Acceleo module in, and
select New > Acceleo Module File
In the wizard, enter the relevant information in the usual fields, then click
on the Advanced >> button
Check the Initialize Content checkbox
Select Copy example content in the listbox below (which should be selected
by default)
select the file that contains the example code
Click on the Finish button
An Acceleo module is created, and the content of the example java file is copied
into this module's primary template.
Note: You may have noticed that error marker at the top left of the Acceleo
editor area. This is due to the package containing the newly created module not
being declared as exported by the plug-in.
It is necessary to add relevant packages to the exported packages list
in your plug-in's MANIFEST.MF file. Especially, templates that contain an
@main annotation and are located in a package which is not exported are
marked with an error marker to remind you that.
Declaring the package as exported by the plug-in removes the error marker, all is
well that ends well.
If you need to generate text corresponding to Acceleo or OCL keywords, you might
need to escape them. This is done by inserting these keywords or symbols within
an Acceleo block and using an OCL String. For example, generating the opening
bracket symbol would require :
[ '[' /]
The same applies for the closing bracket :
[ ']' /]
It may happen that you're using a metamodel which operations or features conflict
with reserved OCL keywords. For example, this happens a lot with UML : if your
metamodel has a class Operation with a feature named body, then writing
myClass.body in OCL will result in a somewhat cryptic error : invalid token
'body'.
The trick to access such features in OCL is to prefix the feature name with an
underscore. For this example, you should have written myClass._body.
For the record, the full list of OCL reserved keywords as per the current OCL
version, 3.0, is as follows :
- and
- body
- context
- def
- derive
- else
- endif
- endpackage
- if
- implies
- in
- init
- inv
- let
- not
- or
- package
- post
- pre
- static
- then
- xor