How to dynamically create Groovy Closing from String in Java

I would like to know how to create an object Closureat runtime from a Java application where the contents of Closure are not known in advance. I found a solution, but I doubt that it is optimal.

Background: I wrote Groovy code that parses a domain-specific language. The parsing code is statically compiled and included in a Java application. In the parser implementation, I have classes acting as delegates for specific DSL sections. These classes are called using the following pattern:

class DslDelegate {
  private Configuration configuration

  def section(@DelegatesTo(SectionDelegate) Closure cl) {
    cl.delegate = new SectionDelegate(configuration)
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
  }
}

I want to call such a method directly from Java code. I can create a new object DslDelegateand then call the method section(). However, I need to create and pass an argument that is an instance Closure. I want the content to be initialized from the object String.

My solution: The following Java code (utility) works, but I ask for improvements. Of course, can this be done in a cleaner or more efficient way?

/**
 * Build a Groovy Closure dynamically
 *
 * @param strings
 *            an array of strings for the text of the Closure
 * @return a Groovy Closure comprising the specified text from {@code strings}
 * @throws IOException
 */
public Closure<?> buildClosure(String... strings) throws IOException {
    Closure<?> closure = null;

    // Create a method returning a closure
    StringBuilder sb = new StringBuilder("def closure() { { script -> ");
    sb.append(String.join("\n", strings));
    sb.append(" } }");

    // Create an anonymous class for the method
    GroovyClassLoader loader = new GroovyClassLoader();
    Class<?> groovyClass = loader.parseClass(sb.toString());

    try {
        // Create an instance of the class
        GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();

        // Invoke the object method and thus obtain the closure
        closure = (Closure<?>) groovyObject.invokeMethod("closure", null);
    } catch (InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    } finally {
        loader.close();
    }

    return closure;
}
+4
source share
2 answers

You can use GroovyShellto create a string Closurefrom strings:

public Closure<?> buildClosure(String... strings) {
    String scriptText = "{ script -> " + String.join("\n", strings) + " }";
    return (Closure<?>) new GroovyShell().evaluate(scriptText);
}
+2
source

@hzpz , . , s code. Let, , String, :

script1 = 'out,a,b,c-> out.println "a=${a}; b=${b}; c=${c}"; return a+b+c;'

String

String.metaClass.toClosure = {
   return (Closure) new GroovyShell().evaluate("{${delegate}}")
}

String .

println script1.toClosure()(out,1,2,3)

println (new File('/folder/script1.groovy')).getText('UTF-8').toClosure()(out,1,2,3)
0

All Articles