blob: 68ed3003b20924cabca267728baf968a0cc88721 [file] [log] [blame]
This section was written to describe the behavior changes between the two versions of Acceleo, for the purpose of migrating from one to the next.
=== How to migrate Acceleo 3 templates to Acceleo 4
==== The migration tool
The migration tool consists on a java standalone utility deployed as a jar file, "migrator.jar". There is a library folder aside containing all of the required jars (Acceleo 3, Acceleo 4) required to perform a migration.
==== Launching a migration
The migration tool requires a fully built Acceleo 3 project as input: the project folder must contain a bin/ folder with all of the compiled (.emtl) versions of the .mtl source files.
The migration jar can be run in command line with the following arguments. We assume we are in the folder containing the migration jar, and that the Acceleo project is at the same level. There is also an empty "output" folder to receive the result of the migration:
----
java -jar migrator.jar <myproject>/<sourcefolder> <output_folder>
----
For instance, for an Acceleo 3 project in which the source templates are in the src/ folder (anywhere in the sub-folders of src/):
----
java -jar migrator.jar myProject/src output
----
The converted .mtl files will be created in the output folder, accordingly to the folder structure in the source project.
=== 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 will 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 just strips 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:* Every template which had duplicated signatures (same name, same argument list, different pre-condition) needs 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 verified manually.
===== 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 will 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 manually transformed into a `let` block at the start of the template (this is not supported by the migration tool).
===== 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.
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.
==== 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`. This is not supported by the migration tool. To translate that in Acceleo 4 you 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 manually converted to an `If` at the start of the for body (this is not supported by the migration tool).
===== 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 manually transformed into a `let` block before the `For` block (this is not supported by the migration tool).
*Note* the initialization block was evaluated before the for itself, and not for every loop.
===== Ranges
Acceleo 4 does not support ranges, e.g.:
----
[for (item : Integer | Sequence{1..5})]
[item/]
[/for]
----
Such for blocks are ignored by the migration tool.
==== 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.
==== ElseLet Blocks
Acceleo 4 does not support elselet blocks, they are ignored by the migration tool.
==== 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 replaces the implicit variable with the correct variable for Acceleo 4.
===== Template
Acceleo 3 : `self` is the first argument of the template.
Acceleo 4 : The migration tool makes the variable explicit, using the first argument of the template
===== Query
Acceleo 3 : `self` is the first argument of the query.
Acceleo 4 : The migration tool makes the variable explicit, using the first argument of the query
===== For
Acceleo 3 : `self` has the same value as the iteration variable.
Acceleo 4 : The migration tool makes the variable explicit, using the iteration variable
===== 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 : The migration tool makes the variable explicit, using the parent context
===== 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 : The migration tool makes the variable explicit, using the parent context
===== 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 : The migration tool makes the variable explicit, using the parent context
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 strips 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 are 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 becomes:
----
[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.
==== Set and Bags
The OCL collection types Set and Bag are not anymore available in AQL, which supports only two types: Sequence and OrderedSet. All collections are ordered.
Thus the migration tool translates each Set into an OrderedSet and each Bag into a Sequence.
=== 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`.
=== Limitations
==== Comments
Comments are mostly ignored by the migration tool, except for module / template / queries documentation.