DSL support
Introduction
A Domain specific language (DSL) allows you to write code more easily for a specific domain, for example xml, json, jpa, etc.The DSL is a compiler feature not a language feature so it can work independently of the language we it is used in and can define it own rules.
Implement
To implement a DSL on or more interfaces can be implemented.| Interface | Description |
|---|---|
| io.github.potjerodekool.nabu.compiler.resolve.spi.ElementResolver | Resolves elements in a DSL |
| io.github.potjerodekool.nabu.compiler.transform.CodeTransformer | Transforms a DSL into Java code |
Register implementations
Implementations of the interfaces can be registered in the plugin.xml file.
<?xml version="1.0" encoding="UTF-8" ?>
<plugin>
<id>io.github.potjerodekool.nabu.plugin.simple</id>
<version>0.0.1</version>
<description>Simple plugin for Nabu compiler</description>
<extensions>
<code-transformer implementationClass="io.github.potjerodekool.nabu.plugin.simple.transform.SimpleTransformer"/>
<element-resolver implementationClass="io.github.potjerodekool.nabu.plugin.simple.transform.SimpleElementResolver"/>
</extensions>
</plugin>
DSL example
Take for example JPA. Writing a Predicate quickly results in hard to read code like this:
fun findCompanyByEmployeeFirstName(employeeFirstName: String): Specification<Company> {
return (c : Root<Company>, q: CriteriaQuery<?>, cb: CriteriaBuilder) -> {
var e = (InnerJoin<Company, Employee>) c.employees;
var e = c.join(Company_.employees, JoinType.INNER);
return cb.equal(e.get(Employee_.FIRST_NAME), employeeFirstName);
};
}
With a DSL you could write more readable code like this:
fun findCompanyByEmployeeFirstName(employeeFirstName: String): Specification<Company> {
return (c : Root<Company>, q: CriteriaQuery<?>, cb: CriteriaBuilder) -> {
var e = (InnerJoin<Company, Employee>) c.employees;
return e.firstName == employeeFirstName;
};
}
Were a join is defined with a cast using a DSL class (InnerJoin in this example)
and access the fields of the entity directly.
The code is transformed to the code below.
As you can see the code doesn't use the static metamodel classes
since the plugin has access to the code and can check if fields exists or not.
fun findCompanyByEmployeeFirstName(employeeFirstName: String): Specification<Company> {
return (c : Root<Company>, q: CriteriaQuery<?>, cb: CriteriaBuilder) -> {
var e = c.join("employees", JoinType.INNER);
return cb.equal(e.get("firstName"), employeeFirstName);
};
}