Question

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()
Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top