I am looking to evaluate math expressions from String values using groovy in java code. I have created groovy script like below:
def sum(List<MyObject> myObjList) { int sum =0 myObjList.each {it -> sum += it.grade} return sum }
The class MyObject is defined as:
public class MyObject { private String name; private String description; private double grade; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public double getGrade() { return grade; } public void setGrade(double grade) { this.grade = grade; } }
The code to evaluate the expression is:
public class Test { public static void main(String[] args) throws IOException { List<MyObject> objects = new ArrayList<>(); MyObject myObject = new MyObject(); myObject.setGrade(2.0); objects.add(myObject); myObject = new MyObject(); myObject.setGrade(1.0); objects.add(myObject); ClassPathResource resource = new ClassPathResource("/groovy-scripts/functions.groovy",Test.class); File file = resource.getFile(); Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); Script script = shell.parse(file); binding.setProperty("objects",objects); String formula = "sum(objects)"; Object result = script.evaluate(formula); System.out.println(result); } }
I keep getting the below exception when I try to run the code:
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Script1.sum() is applicable for argument types: (java.util.ArrayList) values: [[com.example.MyObject@3081f72c, com.example.MyObject@3148f668]] Possible solutions: run(), run(), dump(), use([Ljava.lang.Object;), any(), use(java.lang.Class, groovy.lang.Closure) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:71) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:80) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:169) at Script1.run(Script1.groovy:1) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:574) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:612) at groovy.lang.GroovyShell.evaluate(GroovyShell.java:583) at groovy.lang.Script.evaluate(Script.java:210) at com.example.Test.main(Test.java:37)
Please suggest the correct approach to achieve this
Advertisement
Answer
i’m using groovy to implement main
but it’s not far from java.
option 1 : use closures instead of functions
List objects = [ [grade: 111], [grade: 222] ]; GroovyShell shell = new GroovyShell(); //use undeclared closure variables Script functions = shell.parse(''' sum = {List myObjList -> int sum =0 myObjList.each {it -> sum += it.grade} return sum } '''); functions.run(); // all closure variables assigned into binding functions.getBinding().setProperty("objects", objects); Object result = functions.evaluate("sum(objects)"); println("result="+result);
option 2: use script inheritance
List objects = [ [grade: 111], [grade: 222] ]; GroovyShell shell_1 = new GroovyShell(); shell_1.parse(''' def sum (List myObjList) { int sum =0 myObjList.each {it -> sum += it.grade} return sum } ''', "FUNCTIONS"); // give a class name to parsed script def cc = new org.codehaus.groovy.control.CompilerConfiguration(); cc.setScriptBaseClass("FUNCTIONS"); // any parsed scripts will extend FUNCTIONS script class //use shell_1 class loader because it contains FUNCTIONS class definition GroovyShell shell_2 = new GroovyShell(shell_1.getClassLoader(), cc); Binding binding = new Binding(); binding.setProperty("objects", objects); Script formula = shell_2.parse("sum(objects)"); formula.setBinding(binding); Object result = formula.run(); println("result="+result);
option 3: groovy itself could be much shorter and probably you don’t need over-complicated solution
List objects = [ [grade: 111], [grade: 222] ]; Object result = groovy.util.Eval.me("objects", objects, "objects.sum{it.grade}"); println("result="+result);