Experimental DslHelper for more concise DSL support code
As a follow up to the previous DSL article I propose DslHelper, an experimental class to further simplify DSL support code.
Consider this DSL:
architecture {
rules {
"beans-web" {
}
}
}
To implement the "beans-web"() method call (lines 3 to 5) I would have to write this code:
class RulesDelegate {
private Configuration configuration
RulesDelegate(Configuration configuration) {
this.configuration = configuration
}
def methodMissing(String name, Object args) {
if (args.length == 1) {
if (args[0] instanceof Closure) {
Rule rule = new Rule(name)
args[0].delegate = new RuleDelegate(rule)
args[0].resolveStrategy = Closure.DELEGATE_FIRST
args[0]()
this.configuration.addRule rule
} else {
throw new MissingMethodException(name, this.class, args as Object[])
}
} else {
throw new MissingMethodException(name, this.class, args as Object[])
}
}
}
The methodMissing() method (lines 6 to 23) automatically gets called by Groovy whenever a method is not found (missing) on an object. So, by adding missingMethod() to any class you can intercept all calls to methods that do not exist which is exactly what you need to implement a builder.
However, the methodMissing() implementation is less than appealing. I only want to execute my code if I get one Closure object as arguments. But since the method signature of methodMissing() is dictated by Groovy I have to check argument count and type myself.
If only I could implement missingMethod() like this:
def methodMissing(String name, Closure cl) {
Rule rule = new Rule(name)
cl.delegate = new RuleDelegate(rule)
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
this.configuration.addRule rule
}
Not only is this less code, it's also more readable. Alas, Groovy will not call this method. So we need to give Groovy a hand :-)
Enter DslHelper, an experimental class I wrote that supports strongly typed methodMissing() methods!
def methodMissing(String name, Object args) {
return DslHelper.methodMissing(this, name, args)
}
def methodMissing(String name, Closure cl) {
Rule rule = new Rule(name)
cl.delegate = new RuleDelegate(rule)
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
this.configuration.addRule rule
}
By calling DslHelper.methodMissing() in the methodMissing(String,Object) method I delegate the call to methodMissing(String,Closure). To simply things further I can also extend DslHelper:
class RulesDelegate extends DslHelper {
private Configuration configuration
RulesDelegate(Configuration configuration) {
this.configuration = configuration
}
def methodMissing(String name, Closure cl) {
Rule rule = new Rule(name)
cl.delegate = new RuleDelegate(rule)
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
this.configuration.addRule rule
}
}
DslHelper provides the non-static methodMissing(String,Object) method, letting you concentrate on your DSL syntax.
You can override methodMissing() as many times as you want, DslHelper will do its best to match arguments to a method. Take for example this DSL:
architecture {
rules {
"beans-web" {
}
"web-beans"(ignore: true) {
}
}
}
The "web-beans"() method will pass two arguments to methodMissing(): a Map and a Closure (Groovy converts named arguments to a Map object).
You can extend or call DslHelper and concentrate on methodMissing():
class RulesDelegate extends DslHelper {
private Configuration configuration
RulesDelegate(Configuration configuration) {
this.configuration = configuration
}
def methodMissing(String name, Closure cl) {
// implementation
}
def methodMissing(String name, Map params, Closure cl) {
// implementation
}
}
DslHelper will also help you with optional arguments in your DSL syntax:
architecture {
rules {
"beans-web" {
}
"web-beans"(ignore: true) {
}
"io-beans"(silent: true, "option1", "option2") {
}
}
}
To receive these String object add a String[] array to methodMissing() (The Map object must come first). DslHelper will pass the String values as a String[] array, even if only one String value is passed.
class RulesDelegate extends DslHelper {
private Configuration configuration
RulesDelegate(Configuration configuration) {
this.configuration = configuration
}
def methodMissing(String name, Closure cl) {
// implementation
}
def methodMissing(String name, Map params, Closure cl) {
// implementation
}
def methodMissing(String name, Map params, String[] options, Closure cl) {
// implementation
}
def methodMissing(String name, String[] options, Closure cl) {
// added for completeness
}
}
DslHelper is experimental, so use it at your own risk.
Happy coding!
| Attachment | Size |
|---|---|
| DslHelper.groovy | 3.68 KB |
- Login or register to post comments
- 1506 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Andres Almiray replied on Mon, 2008/01/21 - 4:31pm
Steven Devijver replied on Mon, 2008/01/21 - 4:38pm
in response to: aalmiray