indentationacceleo

Acceleo. Indent strategies


I'm testing Acceleo for a project to generate source cobol code. In this languaje the indentation is very important.

I've found a guide to indent strategies, but I think is outdated because it have references to indentSpace and indentTab and I haven't found this instructions in the Acceleo 3.5 version.

I've thought to use a sequence var to store the indent spaces but is very complex to pass this var to all templates.

Do you know better strategie for indentation?, any reference?


Solution

  • Handling new lines, indentation and whitespaces can be tricky sometimes in Acceleo. You can check out this page: http://help.eclipse.org/juno/topic/org.eclipse.acceleo.doc/pages/reference/textproductionrules.html, it presents text rule productions.

    Also, as Acceleo is an implementation of the MOFM2T standard (OMG standard). There is also complementary information in the specification itself: http://www.omg.org/spec/MOFM2T/1.0/PDF/

    As quick answer, the template call 'remember' the indentation used when it is called.

    Here is an example:

    [template public generate(c : Class)]
    [comment @main/]
    [file (c.name, false, 'UTF-8')]
    Class [c.name/]
        [c.ownedAttribute.gen()/] [comment using implicit iterator/]
    [/file]
    [/template]
    
    [template public gen(p : Property)]
    name=[p.name/], type=[p.type.name/]
                                        [comment notice the new line/]
    [/template]
    

    And the same one, but using a for instead of an implicit iterator:

    [template public generate(c : Class)]
    [comment @main/]
    [file (c.name, false, 'UTF-8')]
    Class [c.name/]
        [for (p : Property | c.ownedAttribute)]
        [p.gen()/]
        [/for]
    [/file]
    [/template]
    
    [template public gen(p : Property)]
    name=[p.name/], type=[p.type.name/] [comment no more new line/]
    [/template]
    

    Here is the produced output (in both cases):

    Class MyClass
        name=attribute, type=MyInterface
        name=attribute2, type=Boolean
        name=attribute3, type=Char
    

    EDIT> Regarding your new question about indentation (on the next answer of this thread). You can easily manage indentation as part of a template, but usually, you have to avoid find a generic template at all cost otherwise, this will often leads to a difficult script to read and maintain.

    Here is a small example, still using UML as input metamodel using recursive templates.

    The input test.uml

    <uml:Model xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uml="http://www.eclipse.org/uml2/4.0.0/UML" xmi:version="2.0" xmi:id="_wfUKcIkNEeWSIKoWblTvZg" name="HelloModel">
      <packageImport xmi:id="_wfUKc4kNEeWSIKoWblTvZg">
        <importedPackage href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#/"/>
      </packageImport>
      <packageImport xmi:id="_wfUKdokNEeWSIKoWblTvZg">
        <importedPackage href="pathmap://GENMYMODEL_LIBRARIES/GenMyModelPrimitiveTypes.library.uml#/"/>
      </packageImport>
      <packagedElement xsi:type="uml:Package" xmi:id="_wfUKeYkNEeWSIKoWblTvZg" name="TOP">
        <packagedElement xsi:type="uml:Class" xmi:id="_wfUKfIkNEeWSIKoWblTvZg" name="Class1">
          <ownedAttribute xmi:id="_wfUKf4kNEeWSIKoWblTvZg" name="a1">
            <type xsi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#//Boolean"/>
          </ownedAttribute>
          <ownedAttribute xmi:id="_wfUKgokNEeWSIKoWblTvZg" name="a2">
            <type xsi:type="uml:PrimitiveType" href="pathmap://GENMYMODEL_LIBRARIES/GenMyModelPrimitiveTypes.library.uml#//Char"/>
          </ownedAttribute>
        </packagedElement>
        <packagedElement xsi:type="uml:Package" xmi:id="_wfUKhYkNEeWSIKoWblTvZg" name="INNER">
          <packagedElement xsi:type="uml:Class" xmi:id="_wfUKiIkNEeWSIKoWblTvZg" name="Class2">
            <ownedAttribute xmi:id="_wfUKi4kNEeWSIKoWblTvZg" name="a1">
              <type xsi:type="uml:PrimitiveType" href="pathmap://GENMYMODEL_LIBRARIES/GenMyModelPrimitiveTypes.library.uml#//Double"/>
            </ownedAttribute>
            <ownedAttribute xmi:id="_wfUKjokNEeWSIKoWblTvZg" name="a2">
              <type xsi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#//Integer"/>
            </ownedAttribute>
          </packagedElement>
        </packagedElement>
      </packagedElement>
    </uml:Model>
    

    Visual representation is here (you can also browse the model tree): https://repository.genmymodel.com/vincent.aranega/test1

    And the script I used:

    [module example_stack('http://www.eclipse.org/uml2/4.0.0/UML')/]
    
    [template public generate(m : Model)]
    [comment @main/]
    [file ('res.txt', false, 'UTF-8')]
    [m.name/]
    ['['/]
        [for (p : Package | m.packagedElement.oclAsType(Package))   separator('\n,\n')]
        [p.gen()/][/for]
    
    ]
    [/file]
    [/template]
    
    [template public gen(o : OclAny)/]
    [template public gen(p : Package)]
    Container [p.name/]
    ['['/]
        [for (o : OclAny | p.packagedElement)]
        [o.gen()/][if (p.packagedElement->asOrderedSet()->last() <> o)],[/if]
    
        [/for]
    ]
    [/template]
    
    [template public gen(c : Class)]
    Module [c.name/]
    ['['/]
        In
        ['['/]
            [for (p : Property | c.ownedAttribute)]
            [p.gen()/][if (c.ownedAttribute->last() <> p)],[/if]
            [/for]
        ]
    ]
    [/template]
    
    [template public gen(p : Property)]
    [p.name/] : [p.type.name/]
    [/template]
    

    And the output:

    HelloModel
    [
        Container TOP
        [
            Module Class1
            [
                In
                [
                    a1 : Boolean,
                    a2 : Char
                ]
            ],
    
            Container INNER
            [
                Module Class2
                [
                    In
                    [
                        a1 : Double,
                        a2 : Integer
                    ]
                ]
    
            ]
    
        ]    
    ]
    

    The idea is to produce a dedicated template for each element instead of only one generic. This way, you control the recursive call and the indentation. In this script, there is one thing I'm not proud of though, this is the way the ',' are managed (using if instead of separator). There is probably a better way to do this.