An Active Code Template definition consists of a python or java file found in a variant. It generally follows the text that needs to be generated, involving the navigation through the UML model, extraction of the information from it into the result text, and so on. Active code templates can refer to each other, to allow a better separation of concerns.
Let’s take the “AttributeGen.py” file as an example to show how a python ACT works. It is part of the standard variant, found in C++ Designer’s resources.
The start of an ACT
Every ACT file starts with a comment zone like this:
1#
2# Template AttributeGen
3# Binded variables
4# CXX facilities for Cxx specific production and navigation
5# GEN facilities for code production
6# MDL facilities for model navigation and condition testing
7# ENG ACT ENG (used to call another template))
8#
9##############################################################################
10##
11
12# Local utilities functions
13# naming rules:
14# - if the utility function directly prints out the generated code it must be named printXXXXX
15# where XXXX is expected to summarize the function role
16# - if the utility function returns a piece of generated code as a
17
18string it must be named makeXXXXXX
19# where XXXX is expected to summarize the function role
20# - if the utility function is used to get some elements of the model and return a list of model elements (navigation convinience)
21# it must be named modelGetXXXXX where XXXX is expected to summarize the function role
22# other rules:
23# - any utility function takes a model element as first parameter (name it el)
24# - utility function must not define global variables of their own
25#
This comment indicates: * the current template’s name, AttributeGen * the available global variables for all python files, corresponding to service classes from C++ Designer, CXX, GEN, MDL and ENG * several rules about how functions in an ACT are created
The python module’s declaration then begins:
1from com.modeliosoft.modelio.cxxdesigner.engine.act import IAct
2from com.modeliosoft.modelio.api.model import *
3from com.modeliosoft.modelio.api.mda.model import ObUtils
4from java.util import ArrayList
5import act
6
7INDENT = _" "_ # four white spaces
8
9class **AttributeGen** (IAct):
10
A python ACT file must implement the com.modeliosoft.modelio.cxxdesigner.engine.act.IAct interface to become available in the engine. It defines only the main entry point of the ACT, which will be described below.
The run function
When evaluating an ACT file, the C++ Designer’s engine always launches run:
1##############################################################################
2#
3# Generation code
4#
5
6 def run(_self_, ctx, attribute):
7 out = ctx.getOutputs()[0]
8 self.printAttributeDeclaration(out, attribute)
9
Three parameters are given to this function:
The first thing to do in the run function is usually to extract the current output from the execution context.
We then have to analyse the given model element to determine which code must be generated. In this example, it is done in a separate utility function.
Utility functions
1def **makeMemberDeclaration**(_self_, att):
2 # standard case
3 # declaration syntax is build as:
4 # decl = $specifiers $decoratedtype $namespacedname$bindings $init;
5 # decoratedtype = $containerpointers $container($type)
6 # type = $basetype $pointers
7 #
8 # example:
9 # static std::vector<int*> C1::att;
10
11 # compute declaration
12 decoratedtype=GEN.makeHxxSignature(att)
13
14 # compute bindings
15 bindings=""
16 if (ObUtils.isTagged(att, "Cxx.Bind")):
17 bindings = "<"
18 for p in ObUtils.getTagValues(att, "Cxx.Bind"):
19 bindings = bindings + p + ", "
20 bindings = bindings.rstrip(", ")
21 bindings = bindings + ">"
22
23 # final assembly
24 decl = decoratedtype + bindings
25
26 return decl
27
28 def printAttributeDeclaration(self, out, el):
29 if act.hasDocumentation(el):
30 out.println(act.makeDocumentationComment(el))
31 out.print(INDENT + _self_.makeMemberDeclaration(el) + ";")
32
Utility functions are the core of the generation process, as they contain the code generation itself.
The naming rule quoted earlier is applied on those two functions; the first one creates a string from an attribute, whereas the second writes content in the output.
When customizing C++ generation, you only have to modify those functions and include the behaviour you need.
Calling another ACT
Active code templates are often divided into several sub-templates, to allow a better understanding and separation of concerns. The ACT engine, given as a global variable (ENG), allows another template to be run and its result obtained. Let’s take a part of another python file as an example,
ClassGen:
1 def printBodyContent(self, out, el):
2 out.println()
3 self.printUseIncludes(out, el)
4 out.println (_"//class header file"_)
5 out.printf("#include \"%s\"\n", [CXX.makeHeaderFilename(el)])
6 out.println()
7 self.printBodyAutoIncludes(out, el)
8 self.printIncludeNotes(out, el)
9 self.printBodyTopNotes(out, el)
10 self.printOpenNamespace(out, el)
11 out.println(ENG.evalAct("ClassDefinitionCxx", el))
12 self.printCloseNamespace(out, el)
13 self.printBodyBottomNotes(out, el)
14
The printBodyContent method is the main part of the body file generation. Includes and notes are produced by several utility functions in ClassGen, but the core of the class content is generated by the ClassDefinitionCxx ACT.
The ENG.evalAct method executes this ACT on the given element, and returns its result (i.e. everything that had been printed in the output), which is itself printed in the current body file.