Reputation: 277
I am trying to create a Groovy class dynamically. I am using GroovyClassLoader and SimpleTemplateEngine to inject field and method definitions and generate my new class text representation and pass it to GroovyClassLOader.parseClass(). This works for class fields and also works for methods if the closures representing method bodies are injected as text. My problem is that I have no idea how to convert a Groovy closure to its text representation. I have found a couple examples here on StackOverflow:
print the closure definition/source in Groovy
and
Getting the contents of closure, in groovy
but both examples give me this exception:
Caught: java.lang.NullPointerException: Cannot invoke method getDeclaredMethods() on null object
which means that metaClass.classNode is null
Here is my script:
c1 = '{return p1 + p2}'
c2 = '{return p1 * p2}'
data = [fields: ['p1': 'int', 'p2':'int'], methods: ['m1': c1, 'm2':c2], name: 'Agent']
templateText = '''
class $name
{
<%fields.each {%> $it.value $it.key \n<% } %>
<%methods.each {%> def $it.key() $it.value \n<% } %>
}
'''
engine = new groovy.text.SimpleTemplateEngine()
template = engine.createTemplate(templateText)
result = template.make(data)
println result
GroovyClassLoader loader = new GroovyClassLoader()
Class cls = loader.parseClass(result.toString())
i = cls.newInstance()
i.p1 = 1
i.p2 = 2
i.setP2(10)
println i.m1()
println i.m2()
Upvotes: 0
Views: 3092
Reputation: 171114
Using the second example you linked to, I can get this to (I think) work:
import groovy.inspect.swingui.AstNodeToScriptVisitor
String method( Closure a ) {
new StringWriter().with { writer ->
a.metaClass.classNode.getDeclaredMethods("doCall")[0].code.visit new AstNodeToScriptVisitor( writer )
"{${writer.toString()}}"
}
}
c1 = {p1 + p2}
c2 = '{return p1 * p2}'
data = [fields: ['p1': 'int', 'p2':'int'], methods: ['m1': method( c1 ), 'm2':c2], name: 'Agent']
templateText = '''
class $name
{
<%fields.each {%> $it.value $it.key \n<% } %>
<%methods.each {%> def $it.key() $it.value \n<% } %>
}
'''
engine = new groovy.text.SimpleTemplateEngine()
template = engine.createTemplate(templateText)
result = template.make(data)
println result
GroovyClassLoader loader = new GroovyClassLoader()
Class cls = loader.parseClass(result.toString())
i = cls.newInstance()
i.p1 = 1
i.p2 = 2
i.setP2(10)
println i.m1()
println i.m2()
It does not work in the GroovyConsole, but it works if you save it as a script, and invoke it from the command line
Upvotes: 1