blob: eab53468713089e7dd63492fc88fad3786c972fb [file] [log] [blame]
= Acceleo
Acceleo 3 to Acceleo 4 migration guide
:doctype: book
:source-highlighter: rouge
:listing-caption: Listing
:toc:
:toclevels: 3
:sectnums:
:icons: image
== Preface
=== Intended Audience
This guide was written to describe the behavior changes between the two versions of Acceleo, for the purpose of migrating from one to the next.
== Language Changes
=== Modules
==== module name
Acceleo 3 allowed users to set the fully qualified name of the module in its declaration, such as
----
[module qualified::module::name(...)]
----
Acceleo 4 forbids anything other than the actual module name and this should thus become.
----
[module name(...)]
----
Both versions require the module name to be equal to the name of the containing file minus the extension.
==== multiple inheritance
The acceleo 3 module declaration allowed users to declare multiple "extends" modules:
----
[module qualified::module::name(...) extends another::module, and::a:second::module]
----
Acceleo 4 modules can only define a single extended module.
*Only the first extended module was taken into account* in Acceleo 3 so the migration can just strip all but the first extended modules' name.
==== module imports
Acceleo 3 allowed imports of modules through unqualified names. This is forbidden in Acceleo 4 and the fully qualified names of the imports are required.
=== Templates
*Important notes:* The Migration tool needs to somehow mark every template which had duplicated signatures (same name, same argument list, different pre-condition) to be manually checked after migration.
* Acceleo 4 does not allow duplicated signatures and has no pre-condition, so duplicated templates might have to be combined into a single one with their pre-condition as an `If-Else` block inside.
* Initialization blocks could be different on duplicated templates, depending on the pre-condition. The migrated `Let` block thus needs to be different per branch of the above `If-Else`.
Acceleo 4 templates will automatically override their super-module templates if the signature matches. This was not the case in Acceleo 3 and matching templates thus need to be marked for manual verification.
==== Overriding
Acceleo 3 templates allowed users to override an arbitrary template through the "overrides" keyword:
----
[template public aTemplateName(...) overrides anotherTemplateName]
----
Acceleo 4 only allows a template of name "xyz" to override a template from the extended module with the same "xyz" name.
Furthermore, Acceleo 4 templates will automatically override their super-module's public or protected templates if they have the same signature.
==== Pre-conditions
Acceleo 4 does not allow pre-conditions on templates.
==== Post-treatment
Acceleo 3 used an implicit String-typed variable so the post expression could be a simple call without a variable:
----
[template public aTemplateName(...) post (trim())]
----
Acceleo 4 doesn't allow implicit variables. The result of the template call will be stored in the `self` variable and the migration must thus transform this expression into:
----
[template public aTemplateName(...) post (self.trim())]
----
==== Init block
Acceleo 3 allowed variable initialization blocks on the template:
----
[template public aTemplateName(...) {var1 = 'string'; var2 = param1.feature;}]
----
Acceleo 4 removes the initialization blocks altogether, so the variable declarations must be transformed into a `let` block at the start of the template.
==== Namesakes
Acceleo 3 allowed multiple templates with the same name and same argument list to co-exist within the same module, as long as their pre-condition differed.
PENDING choice, remove one of the two:
Though Acceleo 4 will allow multiple templates with the same signature to exist in a module, only the last one will ever be resolved for a call or override, all subsequent being ignored.
Acceleo 4 does not allow multiple templates with the same signature in a given module.
=== Query
==== Java services
The best practice for Acceleo 3 was to wrap java services inside of a query. The special `invoke` service was used to this end.
----
[query public hasStereotype(element : uml::Element, stereotypeName : String) : Boolean =
invoke('org.eclipse.acceleo.module.sample.services.UMLServices', 'hasStereotype(org.eclipse.uml2.uml.Element, java.lang.String)', Sequence{element, stereotypeName})
/]
----
Acceleo 4 allows users to directly import java services.
=== File Block
The second argument for the file block in Acceleo 3 was a boolean (false = overwrite the file, true = append at the end of the file).
Acceleo 4 uses an enumeration, which allows the keyword "overwrite", "append" or "create" instead.
=== For Block
==== Simplified Syntax
Acceleo 3 allowed for loops in two formats:
----
[for (i : E | expr)]...[/for]
----
----
[for (expr)]...[/for]
----
In the second case, the loop variable was `self`.
Acceleo 4 only allows for the first of these two formats. Note that typing the iteration variable is optional in Acceleo 4.
==== Iteration count
Acceleo 3 defined an implicit variable, `i`, that held the current iteration count.
----
[for (feature : ecore::EStructuralFeature | class.eStructuralFeatures)]
iteration number [i/]
[/for]
----
Acceleo 4 does not define any similar variable.
==== before, separator, after
Acceleo 3 allowed users to specify a `before` expression that would be inserted right before the content generated by the loop body _if the loop had any iteration_. An `after` that would similarly inserted after the loop body if it generated any content, and finally a `separator` which content would be inserted in-between each iteration result.
----
[for (number : Integer | Sequence{1, 2, 3}) before ('int[] array = new int[') separator (', ') after ('];')][number/][/for]
----
Acceleo 4 only supports `separator`. The migration could convert `before` and `after` into a `Let` holding the content of the iteration expression, then a `If` only generating before and after if the collection is not empty.
==== Pre-condition
Acceleo 3 allowed users to specify a pre-condition that would be evaluated every iteration and that would prevent all generation for that iteration if `false`.
----
[for (number : Integer | Sequence{1, 2, 3}) ? (isEven(number))]
[number/]
[/for]
----
Acceleo 4 does not have pre-conditions. This could be converted to an `If` at the start of the for body.
==== Init block
Acceleo 3 allowed variable initialization blocks on the for:
----
[for (feature : ecore::EStructuralFeature | class.eStructuralFeatures) {var : String = 'string'; className : String = class.name;}]
[number/]
[/for]
----
Acceleo 4 removes the initialization blocks altogether, so the variable declarations must be transformed into a `let` block before the `For` block.
*Note* the initialization block was evaluated before the for itself, and not for every loop.
=== Let Statement
Acceleo 3 only allowed a single variable per `Let`, forcing users to have multiple nested `Let` blocks to define more. The migration could aggregate multiple nested blocks into one with multiple variables if the nested blocks don't use one of the outer `Let`'s variable.
=== Invocation
Acceleo 3 made use of implicit variables allowing module writers to avoid always specifying the target of an expression or call:
----
[template public generate(class : ecore::EClass)]
[name/] is equivalent to [class.name/] or [self.name/]
[eAllContents()/] is equivalent to [class.eAllContents()/] or [self.eAllContents()/]
[/template]
----
The implicit variable is always `self`, but the value of `self` may not be intuitive in all cases.
The migration will have to take specific care of properly replacing the implicit variable with the correct variable for Acceleo 4.
==== Template
Acceleo 3 : `self` is the first argument of the template.
Acceleo 4 : PENDING
==== Query
Acceleo 3 : `self` is the first argument of the query.
Acceleo 4 : PENDING
==== For
Acceleo 3 : `self` has the same value as the iteration variable.
Acceleo 4 : PENDING
==== If
Acceleo 3 : The value of `self` is not changed within the `if` scope and remains the value of `self` outside of the `if`.
Acceleo 4 : PENDING
==== let
Acceleo 3 : The value of `self` is not changed within the `let` scope and remains the value of `self` outside of the `let`.
Acceleo 4 : PENDING
==== Expression
Acceleo 3/OCL : The value of `self` is defined by the current Acceleo scope and will not be altered by OCL.
Acceleo 4/AQL : PENDING
You can have a look at the MTL to AQL https://www.eclipse.org/acceleo/documentation/aql.html#MigratingfromMTLqueries[migration guide].
=== Module Element Call
==== Template invocation
Acceleo 3 allowed special template calls such as the following:
----
[template public aTemplate()]
[anotherTemplate() before ('inserted before generated body') separator ('in-between') after ('inserted after generated body')/]
[/template]
[template protected anotherTemplate()]
generated body
[/template]
----
Both `before` and `after` expression are handled by Acceleo 3 and will respectively generate their content before and after the callee's generated text. This is true even if the callee does not generate any text.
`separator` is not implemented by the engine so the migration can strip it entirely.
==== Query invocation
Similar to template invocations, query invocations support `before`, `separator` and `after` expressions. None of which is implemented in the Acceleo 3 generation engine so they can all be stripped entirely.
=== Variable
Acceleo 3 supported unqualified type names for the variables.
----
[let var : EPackage = anotherVar.eContainer()]
output text for EPackage named [var.name/]
[/let]
----
Acceleo 4 only accepts qualified types for the classifiers and the above should become:
----
[let var : ecore::EPackage = anotherVar.eContainer()]
output text for EPackage named [var.name/]
[/let]
----
=== Expressions
Acceleo 3 was using OCL as the underlying expression language, while Acceleo 4 is using AQL. Please look at the https://www.eclipse.org/acceleo/documentation/#MigratingfromMTLqueries[AQL Documentation] for more information on migrating OCL expressions to AQL.
== Behavior Changes
=== Modules
==== inheritance behavior
In Acceleo 3, once an overriding module (child) called a public or protected template of its extended module (parent), the execution flow would never come down to the child again until we `returned` out of the callee. This is contrary to other Object-oriented languages in which a `super` template could call down an `overriden` other template from the child when necessary.
If you consider the following simplified modules:
----
[module parentModule()/]
[template public aTemplate()]
[anotherTemplate()/]
[/template]
[template protected anotherTemplate()]
parent behavior
[/template]
----
----
[module childModule() extends parentModule/]
[template public main()]
[aTemplate()/]
[/template]
[template protected anotherTemplate()]
child behavior
[/template]
----
=== Query
==== Validation
In Acceleo 3, the return type of a query was not validated at compile time, so it was very easy for ClassCastExceptions to occur at runtime or for invalid templates to be written with the error only detected at runtime.
For example, the following will fail when we try to generate, but is valid for the compiler:
----
[template public generate(c : ecore::EClass)]
[file (c.name.concat('.java'), false, 'UTF-8')]
[for (attribute : ecore::EAttribute | getFeatures(c))]
attribute name : [attribute.name/] [if (attribute.iD)]is id attribute[/if]
[/for]
[/file]
[/template]
[query private getFeatures(c : ecore::EClass) : Set(ecore::EAttribute) = c.eStructuralFeatures/]
----
The template expected "getFeatures" to return a Set of Attributes, but the actual type is a set of EStructuralFeature. This will fail as soon as we try to generate for a class containing both attributes and references.
Acceleo 4 validates the return type of the query's body expression.
==== Cache
The MTL specification enforces that "A query is required to produce the same result each time it is invoked with the same arguments.". The result of a query call was thus cached in Acceleo 3, and never reevaluated. (This behavior could be disabled through a preference for Acceleo 3.)
Acceleo 4 will always reevaluate the query's body even if the same argument list is passed twice.
=== Let Statement
The Acceleo 3 let statement was equivalent to an "instance of" condition check to enter a block.
If we consider the following let block:
----
[let var : EPackage = anotherVar.eContainer()]
output text for EPackage named [var.name/]
[/let]
----
In Acceleo 3, if the result of evaluating `anotherVar.eContainer()` is of type `EPackage` (the declared type of variable `var`), then this block will output the result of evaluating its body. In any other event, this would output no text and cause no failure as the block would be simply ignored if the types do not match.
In Acceleo 4, this same let block will cause validation errors if the type of `anotherVar.eContainer()` cannot be an `EPackage`.