1
votes

I have a Groovy based DSL in which I want to access variables from declared bindings. Here's my DSL implementation:

class Bar {
    String barVal

    void bar(String bar) {
        this.barVal = bar
    }
}

class Foo {
    Bar bar

    void foo(@DelegatesTo(value = Bar, strategy = Closure.DELEGATE_FIRST) closure) {
        def bar = new Bar()
        def code = closure.rehydrate(bar, null, null)
        code()
        this.bar = bar
    }
}

abstract class MyScript extends Script  {

    Foo dslEntryPoint(@DelegatesTo(value = Foo, strategy = Closure.DELEGATE_FIRST) closure) {
        def foo = new Foo()
        def code = closure.rehydrate(foo, null, null)
        code()
        foo
    }
}

And here's an example of my DSL and how I run it:

def DSL_NOT_WORKING = """
    dslEntryPoint() {
        foo {
            bar magicValue
        }
    }
"""

def DSL_OK = """
    def myMagicValue = magicValue
    dslEntryPoint() {
        foo {
            bar myMagicValue
        }
    }
"""

CompilerConfiguration config = new CompilerConfiguration(CompilerConfiguration.DEFAULT)
config.scriptBaseClass = MyScript.class.name
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(getClass().getClassLoader(), config)
Class<Script> clazz = groovyClassLoader.parseClass(DSL_NOT_WORKING)
Binding binding = new Binding()
binding.setVariable('magicValue', '42')
Script script = InvokerHelper.createScript(clazz, binding)
Foo foo = script.run() as Foo
assert foo.bar.barVal == '42'

As you can see, when I bind the variable with def (DSL_OK), I can access its value in my DSL, whereas otherwise (DSL_NOT_WORKING) I get a

java.lang.NullPointerException: Cannot get property 'magicValue' on null object

What am I doing wrong? How can I access the variable magicValue without the def helper declaration? I think it has something to do with calling rehydrate on the closure where I could pass this instead of null, but this does not seem to work with nested object hierarchies (Foo->Bar).

Thanks, Michael

1

1 Answers

0
votes

The problem was my usage of rehydrate which overrides the owner and delegate with null. Instead of bookkeeping the owner and delegate in the nested object hierarchy, it is much simpler to just use a plain delegate instead:

Foo dslEntryPoint(@DelegatesTo(Foo) closure) {
  Foo foo = new Foo()
  closure.delegate = foo
  closure.call()
  foo
}