EGF Tutorial |
Summary
Generating content such as source code can save you time in your projects and can reduce the amount of tedious redundant programming. Generating can be powerful, but the program that writes the code can quickly become very complex and hard to understand. One way to reduce complexity and increase readability is to use the Eclipse Generation Framework (EGF).
EGF project contains two very powerful tools for generating outputs: The factory component (FC) task factory and the pattern. With the pattern you can use an enhanced template-based approach (current implementation uses JET) that makes it easy to express the output you want to generate.
In this tutorial you will learn how to :
- create patterns
- produce an executable form of the patterns
- execute patterns
- use pattern parameters
- use pattern inheritance
Contributed by Guillaume Brocard, Thales EPM, guillaume.brocard at thalesgroup dot com, February 28, 2008.
Modified for EGF contribution, January 29, 2009.
Before creating our first pattern, you will need the EGF Engine plug-ins version 1.0.0.
If you haven't installed it yet, you can get a fully package version here.
Install it before continuing with this tutorial.
You must also be familiar with the handling of factory components.
If not, make sure you understand the Factory Component Tutorial Part 1.
Finally, it is required that you have read and understood the pattern concepts.
Let's start with the famous example of displaying the message "Hello, world".
So as to create our first example, the following steps are required :
From the workbench menu, select File > New > Factory Component Project to bring up the New Factory Component Project wizard.
Deploy the Viewpoints node and select the Pattern Viewpoint one.
Now that we have an element that can contain a pattern, let us create it.
The Overview page is allowing the user to change both the name and the description of the pattern.
Switch to the implementation page of HelloWorld editor. This page is made of two main sections.
<%@ jet package="pattern.tests" class="HelloWorldGenerator" %>By doing so, we're telling JET compiler to compile our future template in a Java class form, called HelloWorldGenerator and located in package pattern.tests
All JET pattern must implement the generateHeader method.
So as to ease the user experience, a default header is generated by the framework at first generation time, if none is provided.
The generated content would very look like the one we have just entered.
Hello world !Make sure you're adding the extra new line at the end of the message (the one at the end of the first line is eaten at compilation time). Close the displayMessage editor.
There remains one important step. That is tell our pattern what to do with its methods.
This is handled by the Generate method section.
You did not get the opportunity to select generateHeader or generateFooter methods.
There are handled automatically by the pattern framework, whether the pattern is an independent one or not.
A new section has appeared (Selected action properties), this time to display the properties of the new call inserted in the generate method
of our pattern.
Save the pattern editor.
So far, we have been editing the pattern model through a dedicated editor.
The pattern can not be executed in this form directly. Indeed JET is expecting just one template file as something to
eat. So there is a production phase for every pattern we want to use.
This production will create a lot of stuff in the plug-in structure hosting the FC.
Do not add those files to the configuration management yet (if you are asked to).
Now to the production.
There are two ways for producing a pattern.
By default, it is automatically produced at save time.
Save the pattern editor (Ctrl + S or File > Save).
Note that the list of internal methods is now sorted by name.
If this option has been disabled (Main menu Pattern > Produce automatically unchecked), then :
You should end with new content in the generated source folder of your FC project.
We are expecting to find our HelloWorldGenerator class as specified by the generateHeader method of our pattern.
This class should compile ok.
You can open this class to figure out the content of the generated generator.
Nevertheless, you must keep in mind that this class is overwritten by the production phase each time it is invoked.
Make sure you're not modifying it directly. Use the pattern editor instead.
It is time to execute our pattern. The following steps are required :
The contract of Pattern Runner is asking for a reporter.
This is an important element of the execution of a pattern. The goal of the pattern is to generate an output. In our case
a textual output. Then raises the question of what to do with this output. This is the job of the reporter.
A reporter is a class that implements the IPatternExecutionReporter interface. In fact, this is a call-back
that is triggered the result of a pattern execution, and its sub-parts that are likely to generate independent content.
This is the place where you decide what to do with the output content.
For now, we'll be using the default implementation PatternExecutionRepoter that sends the output to EGF loggers.
Fill the context of the Pattern Runner invocation with the following values:
Create a new launch configuration of the Eclipse SDK application enabling all your workspace plug-ins and the deployed ones.
In the newly launched workbench, go to EGF perspective, and to the Factory Components view.
Execute pattern.tests factory. The result (in launching console) should look like the following one :
2008-03-03 15:03:38,717 INFO [Worker-5] - PatternExecutionReporter.patternExecutionFinished(..) _ Start of patternLibrary.1204215413112.38/Pattern.1204215762681.39 Hello world ! End of patternLibrary.1204215413112.38/Pattern.1204215762681.39You can close this workbench now.
We've been displaying a message with pattern, and that's a good start since the template-based approach is producing text.
Maybe a bit of model handling would be interesting too (EGF is supporting the model-driven paradigm).
Well you're supposed not to have skipped the previous parts of this tutorial.
What's more, HelloWorld pattern will likely be changed, so a little backup at this point might sound interesting.
Even better, you could be repeating in 5-10 minutes the previous steps, so as to make sure you did acquire the required
dexterity with patterns (that will provide you with a copy of your HelloWorld pattern).
In this part, we'll be feeding our HelloWorld pattern with models.
This is achieved through the Specification page of the pattern editor.
So open HelloWorld editor (if needed) and go to Specification page.
Adding a new parameter is telling the pattern that we would like it, at execution time, to execute its behavior for
every possible value of this parameter, considering the provided model.
Let's precise this assertion with several new ones :
This is a concept best experienced, so we'll add HelloWorld a pattern, and see what happens.
We've got a fresh new parameter, it's high time we did something with it.
Hello <%= anEclassifier.getName() %> !Just as anEclassifier is accessible in the condition class, it is accessible in the methods of HelloWorld too.
<%@ jet package="pattern.tests" class="HelloWorldGenerator" imports="org.eclipse.emf.ecore.EClassifier" %>Then produce HelloWorld again. It's compiling now (if not, do not forget to save the generateHeader editor before producing again).
The resulting output in the launching console is composed of several such outputs (indeed, one per EClassifier in UML.ecore) :
2008-03-03 17:47:10,379 INFO [Worker-1] - PatternExecutionReporter.patternLoopExecutionFinished(..) _ Start of a loop in patternLibrary.1204215413112.38/Pattern.1204215762681.39 Hello CallBehaviorAction ! End of a loop in patternLibrary.1204215413112.38/Pattern.1204215762681.39And one big :
2008-03-03 17:47:10,379 INFO [Worker-1] - PatternExecutionReporter.patternExecutionFinished(..) _ Start of patternLibrary.1204215413112.38/Pattern.1204215762681.39 Hello ClearStructuralFeatureAction ! Hello TemplateableElement ! Hello Abstraction ! Hello LiteralNull ! Hello Vertex ! Hello CommunicationPath ! Hello CallConcurrencyKind ! Hello ElementImport ! Hello ActivityPartition ! Hello UseCase ! Hello Reception ! ... Hello PackageImport ! Hello CallBehaviorAction ! End of patternLibrary.1204215413112.38/Pattern.1204215762681.39As you might have already guessed, the pattern execution reporter is called at different times.
Imagine that pattern.tests is a FC provided by another team, as a usable COTS.
We are fine with the behavior, but we would like to change the output message.
This is a good opportunity to test the inheritance mechanism.
The following steps are required :
As Eclipse experts, you'll find that part pretty easy to achieve.
Again as EGF experts, that is something easy to do.
Now to the interesting part.
Extended Hello <%= anEclassifier.getName() %>;We do have access to the parent pattern parameters.
<%@ jet package="pattern.test2" class="ExtendedHelloWorldGenerator" imports="org.eclipse.emf.ecore.EClassifier" %>There is a need to import EClassifier because this is an inherited type.
However, you should have noticed that a correct header is already available.
This one was generated by the framework at save time, which occurred after specifying the parent pattern.
This mechanism works for the first production only, when no header is provided.
If one header is found, then nothing happens, and you need to modify it by yourself.
Extended Hello SendOperationEvent;