Introduction
This document introduces the main concepts of the Torque generator. The following concepts are explained:Outlets
A outlet generates String output from sources. An example for an outlet is a VelocityOutlet, which uses a velocity template to create the output. An outlet may be used in several generation processes. The output of an outlet can be used as file name or (part of a) generated file.
Sources
Sources are the input of the generation process. They can be read from different formats, e.g. properties files, XML files, or jdbc metadata. For each generation process, a directed weighted graph (which is usually, but not necessarily, a tree) is constructed from the sources. The graph may then be fed into the outlets, or it may be transformed in an intermediate step. Each outlet can access the transformed graph to produce its output.
Initially (when loaded), the graph is represented similarly
to the structure which is normally used to represent XML files:
The nodes are called "elements", and each element has a list of children
and a list of parents (i.e. parents and children are ordered).
Elements without parents
are also allowed, but usually there will be only one such element
(the "root").
Each element can also have attributes. Each attribute has a name and a
value. There can be at most one attribute with a given name for each
element.
Additionally to the attributes, each element also has a name.
Transformers
In the simplest generation projects, it suffices to pass the sources directly to the outlets. But in more complex projects, it makes sense to transform the source before generating output from it. A transformer performs the transformation from an input model to an output model. The output model is then used for generation. In general, the input model and the output model do not need to have the same structure. For example, it is common to transform the element graph from reading the sources to a typed model. An example is shown below.
A typical example is that different source elements reference each other (e.g. by name, or by even more complex relations), and one would like to have this reference resolved before the generation phase. E.g. the source looks (in XML representation) like
<root> <element name="element1"> <related name="element2"> </element> <element name="element2"/> <element name="element3"/> </root>
<root> <element name="element1"> <related name="element2"> <relatedElements> <element name="element2"/> <-- This is typically element2 itself, not a copy </relatedElements> </element> <element name="element2"/> <element name="element3"/> </root>
<database> <table name="FIRST_TABLE" /> </database>
<database> <table name="FIRST_TABLE" javaName="FirstTable"/> </database>
A third typical example is to transform the graph model to a typed model. Consider again the example source
<database> <table name="FIRST_TABLE" /> </database>
// Database.java public class Database { public List<Table> tableList; } // Table.java public class Table { public String name; }
... <transformer class="org.apache.torque.generator.source.transform.SourceElementToModelTransformer"> <modelRootClass>model.Database</modelRootClass> </transformer> ...
- The attribute types must be concrete classes with a default constructor.
- Each attribute in the source graph must have an associated property in the target model.
- Each child element in the source graph must have an associated property in the target model.
- The name of the associated property must be either the name of the attribute/child element, or the the name of the attribute/child element with suffix "s", or the the name of the attribute/child element with suffix "List". Should the name of the attribute/child element be a reserved java word, the java property name must be prefixed by an underscore (_). (e.g. attribute "default" -> java field "_default")
- The java property can either consist of a public field or a public setter using the JavaBeans conventions. If using setters for arrays or collections, a public getter must also be available in the case more than one element needs to be filled into the array or collection.
SkipDeciders
SkipDeciders do what their name promise, they decide whether the generation of certain files is skipped. E.g. a SkipDecider could read an option value and skip the generation of a file if the option is not set. SkipDeciders are java classes which implement the org.apache.torque.generator.source.skipDecider.SkipDecider interface.
Postprocessors
Postprocessors process the generated content after generation. I.e. they do not know the source model any more, they just operate on the generated text. Postprocessors are java classes which implement the org.apache.torque.generator.processor.string.StringProcessor interface. They are useful for e.g. fixing the line end mode of files (org.apache.torque.generator.processor.string.UnixLinefeedProcessor) or for removing unused imports in java class files (org.apache.torque.generator.processor.string.OrganizeImportsProcessor).
Controller
The Controller makes sure that the configuration files are read, reads the source and options, performs possible transformations on the source and feeds the result into the generators.
Options
Options can be used in the generation process to influence the output. Note that sometimes, there might be an ambiguity between sources and options. The difference is more of a philosophical nature: Sources are the data to be processed, whereas options influence the way the data is processed.
Variables
To ease communication between outlets, the concept of a variable is also known outside of the single outlets. This makes it possible e.g. to pass a variable from a Java outlet to a Velocity template in the same generation process.
Variables have different scopes. The scope defines where a certain variable is visible. The current scopes are "OUTLET" (the variable is only visible in this outlet), "CHILDREN" (the variable is visible to the current outlet and all outlets called by it), "FILE" (the variable is visible during the generation of the current file), and "GLOBAL" (the variable is always visible).
Note that scopes and namespaces are orthogonal concepts, i.e. a variable is visible only if it has a sufficient scope and the correct namespace.
Mergepoints
The Torque generator encourages to use modularized outlets, in order to reuse them or drop, replace or add parts into the generation process. To connect one outlet to another, mergepoints can be used. A mergepoint is a place where the output of one outlet can be fed into another outlet.
If a mergepoint is encountered during the generation process, one or more actions may be performed, depending of the configuration of the current outlet. Typically, these actions describe how to traverse the source tree. An example would be "invoke the outlet xyz for all children of the current element and insert the output here".
If no action is defined at a mergepoint, it is left unused. This should be used to insert extension points in the outlets, as a placeholder for additional output. E.g. if you write a velocity template which generates a java class, it makes sense to create unused extension points where additional methods can be inserted (without changing the template).
Namespaces
If outlets are created in a modular manner, it is desirable that some options and variables are visible only to one outlet, while others should be visible to all outlets. To support this, outlets, options and variables can be assigned a namespace. Namespace elements are separated by dots. Generators see only options and variables which live in their namespace or in parent namespaces.
For example, the outlet with the fully qualified name "org.apache.torque.generator.classnameToFilename" lives in the namespace "org.apache.torque.generator" and will only see variables and options set in that namespace or in the parent namespaces "org.apache.torque", "org.apache", "org" and "". The namespace "" is also called the root namespace, variables and options therein are visible to all outlets.
You do not need to use namespaces; if you do not prefix any name with a namespace, every option and variable will be seen by every template.
Configuration
The configuration of the Torque generator tells the Controller which option files are read, which output files are produced from which Outlets and Sources, which SkipDeciders and which Transformers should be used, and which Outlets are connected to which Mergepoints. The configuration of the Torque generator is described in detail in the configuration section of the Torque generation documentation.
Overriding
The Torque generator offers overriding via a parent-child mechanism. A generator configuration can inherit from another generator configuration; and settings can be overridden or added. This can be used to customize the generation output. A common way to change the output is to add or change Outlets which are used by the generation mechanism. E.g. one can replace a template in all places by overriding an Outlet definition in a child configuration, or one can disable the use of an outlet in a place by overriding a Mergepoint definition.