Developer documentation
If you want to find out how to extend the Generator and adapt it to fit your own needs, this page is for you.
General architecture and workflow
When invoking the Generator, it will (roughly) perform the following actions:
Each of these steps has a corresponding Scala class (called Services hereafter) to take care of it. So, when extending the Generator there are two basic approaches:
-
modifying one of the Services (either directly or by means of subclassing)
-
intercepting the workflow by adding more Services
To give you a rough idea of what Services are available and where they will be located, here is an overview of the Generator’s architecture:
The Scala Abstract class representation
The Abstract class representation (ACR) is the hearth of the Generator. It forms the meta-model for the code to generate and provides facilities to adapt model instances for different purposes.
In short each element of a Scala class has a matching class in the ACR. E.g. a method will be
represented by an instance of SMethod
, attributes become SAttribute
s and a class itself will
be mapped to a SClass
. As the main purpose of the Generator is to provide executable software
models, an abstraction of a model, the SModel
is provided as well. It is pretty straightforward
and simply provides all the classes in the model.
Types and Classes
Models seldom start from scratch. Instead, they will reuse certain data types, such as String
s,
Integer
s, etc. as attributes in the classes that are specified by the model. Usually these data
types are provided by some library and may be mapped to a basic data types of the target programming
language (or a converter is provided along with them).
The Generator follows a similar approach: it provides an abstraction for user-supplied classes (i.e.
the classes specified within the model). This is the SClass
class already mentioned above.
Additionally a SType
class is provided to handle predefined data types. When generating source
code these types are expected to exist "as are" and will not be tackled further. I.e. no .scala
files will be created for SType
instances.
As types and classes are concepts only found at a more abstract level and are irrelevant when it
comes to Scala source code, they share a common superclass - STypedElement
- which will be used
whenever type information (no matter if it is user-supplied or generic) is needed.
Working with the ACR on a more abstract level
The ACR forms a trade-off between an abstract meta-model on one side, and a straightforward (hence
simplified) usage on the other side. Therefore low-level abstraction will oftentimes be wrapped by
some higher abstraction. E.g. a SMethod
consists of abstract SMethodStatement
s and may thus
provide abstract functionality for modification. However a SMethodStatement
simply wraps a
String
(the actual statement). Dealing with the statements themselves may therefore become
pretty cumbersome at a certain point. To circumvent this issue, the ACR's elements should be
extended by means of subclassing to provide more tailored versions which handle the additional
complexity.
A good example for this approach is the extension of the SMethod
class to easily generate
Getters and Setters for attributes:
class SMethod(val name: String,
val result: STypedElement,
val params: Seq[SMethodParameter],
var implementation: Seq[SMethodStatement]) {
// implementation omitted
}
class SGetter(attr: SAttribute) extends SMethod(
name = s"get${attr.name.firstLetterToUpperCase}",
result = attr.attrType,
params = Seq.empty,
implementation = Seq(SMethodStatement(content = attr.name, usedTypes = Set(attr.attrType))) {
// implementation omitted
}
Getters may now be generated by only specifying the attribute they are created for - the cumbersome creation of the actual method statements is hidden within the class implementation.