Developer's guide contents:
Three modules are currently provided:
The generic model hierarchy can be used as-is for simple modeling representations.
The Model interface tries to balance simplicity with functionality and flexibility. Models are arranged into trees. Here is an example from RevGen tool which is based on JavaGen Agile:
/globalDefaults
|
+--root
|
+ database
| |
| +-- table1...
| |
| +-- table2
| |
+ class1 +-- column1...
| |
| +-- property1
| |
| +-- reference1
| |The globalDefaults and root nodes don't actually model anything but serve as a global property holder and a model container respectively. The database model is typically generated by the DbLoader. The classes are generated by RevGen traversing the database tree.
Although not show, the models are highly interlinked, so for example both the table and the entity class that resulted from it are linked to each other. You access these associated models using the context:
Table table = (Table)entityClass.get("table");
Class entityClass = (Class)table.get("entity");By this means models are kept simple and domain-specific so that they can be reused in different contexts.
A given model can result in multiple artifacts so in general they should not contain artifact-specific details, but rather more abstract declarative properties such as: notNull, autoIncrement, linkTable, cardinality, navigable, etc.
Contexts (ie maps of key-value pairs) can be attached to model instances allowing artifact customization in a generic fashion.
Because models are arranged into tree structures, global attributes can be shared from the root node or overridden by local nodes. The context visible at template invocation is the combination of the current context (leaf) down through the parent chain to the root with child values taking precedence over parent values.
Even templates have contexts allowing customization parameters to be passed.
Place-holders allow flexible and consistent naming patterns for class, package, directory and variable names. For example if you define a doaClassNamePattern property and place it in the root context:
rootModel.put("doaClassNamePattern", "${modelName}DaoImpl");Then the name can be bound in the template as follows:
public class ${ooNamingService.className("doaClassNamePattern", model)} implements Serializable {
...If the model is named Address, the resulting class name will be AddressDaoImpl. This works because when a template is invoked, the framework always places the model instance and model name in the context:
currentModel.put("model", model);
currentModel.put("modelName", model.getName());For those of you paying attention, the final detail is the ooNamingService which is placed in the root node at startup:
rootModel.put("ooNamingService", new JavaNamingService());Naming patterns can reference other patterns to build compound names. For example:
rootModel.put("rootPackage", "org/javagen/myapp/");
rootModel.put("doaFullClassNamePattern", "${rootPackage}${doaClassNamePattern});Using the same Address example the following template code:
import ${ooNamingService.packageName("doaFullClassNamePattern", model)}; Will result in the package name: org.javagen.myapp.AddressDaoImpl
JavaGen Agile models can be serialized as XML files providing a means of visualizing and customizing models.
A serialized model can serve as a starting point for customization. The serialized ADDRESS table for example, looks something like this:
<table name="ADDRESS" modelType="TABLE" id="db:ADDRESS">
<pkColumn name="ADDRESS_ID" dbType="INTEGER" modelType="COLUMN" id="db:ADDRESS.ADDRESS_ID"/>
<column name="STREET" dbType="VARCHAR" columnSize="40" modelType="COLUMN" id="db:BIOENTRY.NAME"/>
...
</table>It can be stripped down to it's IDs and used as a template to set or override the primary key autoIncrement property:
<table id="db:ADDRESS">
<pkColumn id="db:ADDRESS.ADDRESS_ID" autoIncrement="true" />
</table>With the exception of the ID property, any property can be overriden in this manner by adding the IdOverrideVisitor generator to the code generation pipeline and passing it the name of the custimized XML file.
At the core of JavaGen Agile is the GeneratorPipeline which implements a code generation pipeline or assembly line. Having a pipelined architecture makes it easy to customize code generation behavior by adding and removing task-specific modules called Generators.
For example, the LinkTableFinderVisitor can be invoked immediately after the database model is constructed. It is used to identify link or association tables whose only function is to link one table to another. In the object-to-relational mapping (ORM) realm, weather or not a table is treated as a link table has a large impact on the resulting code. In RevGen one can simply add or remove LinkTableFinderVisitor from the code generation pipeline to obtain the desired ORM mapping without effecting the rest of the code.
Visitor patterns are defined for each model tree hierarchy type facilitating the creation of task-specific modules (see the IdAssignerDatabaseVisitor for a good example).
Visitors usually implement the Generators interface so they can be used directly in the code generation pipeline.
Template engines are pluggable with both Freemarker and Velocity supported.
Template files are managed and configured using Emitters.
As part of the oo module, the OONamingService provides language-specific naming methods:
String bindName(String templateKey, Model model);
String className(String classNameTemplateKey, Class _class);
String className(Class _class);
String packageName(String packageNameTemplateKey, Model _class);
String fullClassName(String packageNameTemplateKey, String classNameTemplateKey, Class _class);Also supported is plural to singular name conversion. Singular class names result in clearer semantics in relationship naming. Names that can not be singularized by dropping an 's' can be explicitly configured in a lookup table.
String toSingular(String plural);
String toPlural(String singular);A simple predicate language is defined which can be attached to template emitters for conditional code generation.
This can be useful for controlling one-time generation artifacts or in constructing multi-target code generation frameworks.
The goal of JavaGen Agile is to only limit configuration to your imagination. For a good example of what's possible see the configuration section in RevGen.