-
Notifications
You must be signed in to change notification settings - Fork 70
cobigen core_development
CobiGen uses Apache FreeMarker as engine for generation through FreeMarker templates.
Note
|
|
The core implementation are divided in three projects:
-
cobigen-core-api: Mainly composed by interfaces that will be called from the Eclipse plug-in.
-
cobigen-core: The implementation of the interfaces are within.
-
cobigen-core-test: As the name suggests, used for test purposes.
The extension package from the API
project contains the interfaces to be implemented if necessary by the sub plugins:
-
`GeneratorPluginActivator.java`
-
`InputReader.java`
-
`MatcherInterpreter.java`
-
Merger.java
-
`TriggerInterpreter.java`
-
`ModelBuilder.java`
The ModelBuilder
is an interface for accessing the internal model builder instance. Is implemented by `ModelBuilder.java` from the model package from the implementation project that provides the methods to call the createModel()
from the correspondent input reader from the correspondent trigger interpreter to create the object models for a given object.
The to package have the transfer objects of template
, matcher
, increment
and variable assignment
classes that will be used as "communication channel" between the core and sub plug-ins methods
The core must load all the sub plugins to get their Merger, Matcher, TriggerInterpreter
and InputReader
. That elements must implement their respective interfaces from the core.
Is important to note that not all the sub plug-ins need to have implemented a Matcher and/or an InputReader
(advanced information here)
The process of loading plugins to the core is done at the eclipse-plugin initialization.
Each sub plugin has an activator class that extends the `GeneratorPluginActivator` interface from the extension package. That class implements the methods bindMerger()
and bindTriggerInterpreter()
.
This is the class passed as argument to the loadPlugin()
method of `PluginRegister.java` of the pluginmanager package.
This method registers the mergers and the trigger interpreter of the sub plugins to the core. The trigger interpreter has the correspondent input reader of the plugin.
The CobiGen initialization must initialize the context configuration and the FreeMarker configuration
When a CobiGen object is instantiated, the constructor initializes the Freemarker configuration creating a configuration instance from the class freemarker.template.Configuration and adjust its settings.
freeMarkerConfig = new Configuration(Configuration.VERSION_2_3_23);
freeMarkerConfig.setObjectWrapper(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_23).build());
freeMarkerConfig.clearEncodingMap();
freeMarkerConfig.setDefaultEncoding("UTF-8");
freeMarkerConfig.setLocalizedLookup(false);
freeMarkerConfig.setTemplateLoader(new NioFileSystemTemplateLoader(`configFolder`));
Using the FileSystemUtil
from the util package the URI
of the root folder containing the context.xml
and all templates, configurations etc… is converted to a Path object passing it as argument to the ContextConfiguration
constructor.
The ContextConfiguration
creates a new ContextConfiguration
from the config package with the contents initially loaded from the context.xml
Note
|
How the |
The Configuration initialization requires the version of FreeMarker to be used and at the ObjectWrapper
initialization as well.
The DefaultObjectWrapperBuilder
creates an DefaultObjectWrapper
object that maps Java objects to the type-system of FreeMarker Template Language (FTL
) with the given incompatibleImprovements
specified by the version used as argument.
The configuration of FreeMarker requires to specify to a `TemplateLoader`. A `TemplateLoader` is an interface provided by FreeMarker library that the developer should implement to fit the needs. The `TemplateLoader` implementation at CobiGen is the class `NioFileSystemTemplateLoader.java` from the config.nio package.
The context configuration reads the context.xml
file from the template project (default: CobiGen_Templates) passing the path as argument to the constructor. At the constructor, it is created an instance of ContextConfigurationReader.java
from the config.reader package.
Note
|
Please, check the CobiGen configuration for extended information about the |
That reader uses the JAXB, JAXB (Java Architecture for XML Binding) provides a fast and convenient way to bind XML schemas and Java representations, making it easy for Java developers to incorporate XML data and processing functions in Java applications. As part of this process, JAXB provides methods for unmarshalling (reading) XML instance documents into Java content trees.
<<<<<<< HEAD
JAXB auto generates the Java object within the JAXBContext
specified at the xmlns
attribute of the contextConfiguration
field from the context.xml
file
Unmarshaller unmarshaller = JAXBContext.newInstance(ContextConfiguration.class).createUnmarshaller();
That auto-generation follows the contextConfiguration.xsd
schema. Each Java object follows the template specified with the field <xs:CompleType>
from the schema file.
<xs:complexType name="trigger">
<xs:sequence>
<xs:element name="containerMatcher" type="tns:containerMatcher" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="matcher" type="tns:matcher" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" use="required" type="xs:NCName"/>
<xs:attribute name="type" use="required" type="xs:string"/>
<xs:attribute name="templateFolder" use="required" type="xs:string"/>
<xs:attribute name="inputCharset" use="optional" type="xs:string" default="UTF-8"/>
</xs:complexType>
<xs:complexType name="matcher">
<xs:sequence>
<xs:element name="variableAssignment" type="tns:variableAssignment" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="type" type="xs:string" use="required"/>
<xs:attribute name="value" type="xs:string" use="required"/>
<xs:attribute name="accumulationType" type="tns:accumulationType" use="optional" default="OR"/>
</xs:complexType>
The generated Java objects has the elements and attributes specified at the schema:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "trigger", namespace = "http://capgemini.com/devonfw/cobigen/ContextConfiguration", propOrder = {
"containerMatcher",
"matcher"
})
public class Trigger {
@XmlElement(namespace = "http://capgemini.com/devonfw/cobigen/ContextConfiguration")
protected List<ContainerMatcher> containerMatcher;
@XmlElement(namespace = "http://capgemini.com/devonfw/cobigen/ContextConfiguration")
protected List<Matcher> matcher;
@XmlAttribute(name = "id", required = true)
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
@XmlSchemaType(name = "NCName")
protected String id;
@XmlAttribute(name = "type", required = true)
protected String type;
@XmlAttribute(name = "templateFolder", required = true)
protected String templateFolder;
@XmlAttribute(name = "inputCharset")
protected String inputCharset;
...
..
.
}
This process it is done when calling the unmarshal()
method.
Object rootNode = unmarshaller.unmarshal(Files.newInputStream(contextFile));
Note
|
For extended information about JAXB check the offical documentation. |
If the version retrieved after the unmarshal
process is null, an InvalidConfigurationException
defined at exceptions package will be thrown.
If it is not null, will be compared using the validate()
method from `VersionValidator.java` from config.versioning package with the project version retrieved by the `MavenMetadata.java`. The `MavenMetadata.java` file is provided by the POM while building the JAR
file
<build>
<plugins>
<!-- Inject Maven Properties in java-templates source folder -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-version-class</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
...
..
.
</plugins>
</build>
MavenMetadata
gets the current CobiGen version by reading the <version>
label inside the <project>
label from the POM file
public class MavenMetadata {
/** Maven version */
public static final String VERSION = "${project.version}";
}
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<artifactId>cobigen-core</artifactId>
<name>CobiGen</name>
<version>2.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
...
..
.
}
The comparison has three possibilities:
-
Versions are equal → Valid <<<<<<< HEAD
-
context.xml
version is greater than current CobiGen version →InvalidConfigurationException
-
Current CobiGen version is greater that
context.xml
version → Compatible if there not exists a version step (breaking change) in between, otherwise, throw an error.
Reaching this point, the configuration version and root node has been validated. Unmarshal with schema checks for checking the correctness and give the user more hints to correct his failures.
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
ContextConfigurationVersion latestConfigurationVersion = ContextConfigurationVersion.getLatest();
try (
InputStream schemaStream = getClass().getResourceAsStream("/schema/" + latestConfigurationVersion
+ "/contextConfiguration.xsd");
InputStream configInputStream = Files.newInputStream(contextFile)) {
Schema schema = schemaFactory.newSchema(new StreamSource(schemaStream));
unmarshaller.setSchema(schema);
rootNode = unmarshaller.unmarshal(configInputStream);
contextNode = (ContextConfiguration) rootNode;
}
To finish the context configuration initialization, the, trigger, matchers, container matchers, accumulation types and variables assignments are retrieved from the correspondent Java objects generated by JAXB.
public Map<String, Trigger> loadTriggers()
private List<Matcher> loadMatchers(Trigger trigger)
private List<ContainerMatcher> loadContainerMatchers(Trigger trigger)
private List<VariableAssignment> loadVariableAssignments(Matcher matcher)
Depending on the input, the generation process can begin from two different generate()
methods called at the CobiGenWrapper
from the eclipse-plugin:
public void generate(TemplateTo template, boolean forceOverride) throws IOException, TemplateException, MergeException {
if (singleNonContainerInput) {
Map<String, Object> model = cobiGen.getModelBuilder(inputs.get(0), template.getTriggerId()).createModel();
adaptModel(model);
cobiGen.generate(inputs.get(0), template, model, forceOverride);
} else {
for (Object input : inputs) {
cobiGen.generate(input, template, forceOverride);
}
}
}
If the input is a single non container input, first step is to create the model, then allow customization by the user (adaptModel()
) and finally call the generate()
method from CobiGen using the input, template, model and the boolean forceOverride
.
The generation process in this case will follow this main steps:
-
Check if the input is not null
-
Get the trigger interpreter for the type of the trigger of the template
-
Set the root folder for the templates to use for the generation
-
Get the input reader for the trigger interpreter retrieved
-
Test if the input is a package.
This only can be possible in the case of java inputs. As the input is a single non container input, this check will fail and the execution will continue. -
Check if the model parameter is null and if it is, create a new model
As the model has been created at theCobiGenWrapper
, there is no need to create it again. -
Get the destination file.
-
Check if the destination file already exists
If it exists, but theforceOverride
is set totrue
or the merge strategy of the template is null, the file will be overwritten, not merged. Otherwise, first generate output into a writter object, get the merger and merge the original file with the writter and write the file with the merge result. -
If the file does not exist, simple write the file.
The other case is, or the input is multiple files selection, the generation process will be performed for each individual file of the selection, but the model will be created at the step 6 of the steps of the Single Non Container Input and not allowing the user customization.
Disclaimer
If you discover any documentation bugs or would like to request new content, please raise them as an issue or create a pull request. Contributions to this wiki are done through the main repo under the folder documentation.
License
This documentation is licensed under the Creative Commons License (Attribution-NoDerivatives 4.0 International
)