Currently I am working on a way to run java code in string form. So here is how I did it.
import java.util.HashMap;
import java.util.Map;
import groovy.lang.GroovyClassLoader;
public class GroovyStackOverflow {
public static void main(String[] args) {
GroovyClassLoader gcl = new GroovyClassLoader();
String codeSnippet = "double calculatedAnswer = (Double)"
+ "contextMap.get(\"doubleValue\") * (Double)contextMap.get(\"doubleValue\");"
+ " calculatedAnswer = Math.sqrt(calculatedAnswer); "
+ "calculatedAnswer = calculatedAnswer * calculatedAnswer;"
+ "System.out.println(calculatedAnswer);"
+ " return calculatedAnswer;";
StringBuilder sb = new StringBuilder();
sb.append("public class ScriptImplementor implements ScriptEvaluator { public Object evaluate(Map contextMap) {");
sb.append(codeSnippet);
sb.append("} }");
Class<?> clazz = gcl.parseClass(sb.toString());
ScriptEvaluator scriptEvaluator = null;
double calculatedAnswer = 100.0;
try {
Map contextMap = new HashMap();
contextMap.put("doubleValue", (double)100.0);
contextMap.put("threadId", "thread"+100);
contextMap.put("hashCode", 100);
scriptEvaluator = (ScriptEvaluator) clazz.newInstance();
scriptEvaluator.evaluate(contextMap);;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
public interface ScriptEvaluator {
public Object evaluate(Map contextMap);
}
Problem is that it fails in following case.
import java.util.HashMap;
import java.util.Map;
import groovy.lang.GroovyClassLoader;
public class GroovyStackOverflow {
public static void main(String[] args) {
GroovyClassLoader gcl = new GroovyClassLoader();
String codeSnippet = "double calculatedAnswer = (Double)"
+ "\n "
+ "contextMap.get(\"doubleValue\") * (Double)contextMap.get(\"doubleValue\");"
+ " calculatedAnswer = Math.sqrt(calculatedAnswer); "
+ "calculatedAnswer = calculatedAnswer * calculatedAnswer;"
+ "System.out.println(calculatedAnswer);"
+ " return calculatedAnswer;";
StringBuilder sb = new StringBuilder();
sb.append("public class ScriptImplementor implements ScriptEvaluator { public Object evaluate(Map contextMap) {");
//sb.append(codeSnippet.replaceAll("\n", " "));
sb.append(codeSnippet);
sb.append("} }");
Class<?> clazz = gcl.parseClass(sb.toString());
ScriptEvaluator scriptEvaluator = null;
double calculatedAnswer = 100.0;
try {
Map contextMap = new HashMap();
contextMap.put("doubleValue", (double)100.0);
contextMap.put("threadId", "thread"+100);
contextMap.put("hashCode", 100);
scriptEvaluator = (ScriptEvaluator) clazz.newInstance();
scriptEvaluator.evaluate(contextMap);;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
public interface ScriptEvaluator {
public Object evaluate(Map contextMap);
}
I don't understand why it fails and what this error message means-
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'class java.lang.Double' with class 'java.lang.Class' to class 'double'
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToNumber(DefaultTypeTransformation.java:163)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.doubleUnbox(DefaultTypeTransformation.java:88)
at ScriptImplementor.evaluate(script15126616543572010791987.groovy:1)
at GroovyStackOverflow.main(GroovyStackOverflow.java:33)
After uncommenting this code //sb.append(codeSnippet.replaceAll("\n", " "));
it works. But please suggest a better way to handle it. Also why it does not give error while parsing class?
And what other surprises I can expect like this?
You hit a difference between Java and Groovy.
In Java a statement is ended by a semicolon.
In Groovy a satement is ended by a semicolon, or by a linebreak if the statement already is a complete statement.
In your case this means the code
double calculatedAnswer = (Double)
contextMap.get("doubleValue") * (Double)contextMap.get("doubleValue")
is two statements.
The first of these statements is double calculatedAnswer = (Double)
.
In Groovy you can also omit .class
to reference a class, so Double.class
can be written as Double
.
So what you do in that statement is, that you assign the Double
class object to a double
variable. The parentheses are just no-ops here.
This of course fails like the message says, as the Double
class object cannot be case automatically to a double
.
You can explicitly escape a linebreak to make it not end a statement like in
double calculatedAnswer = (Double)\
contextMap.get("doubleValue") * (Double)contextMap.get("doubleValue")
which would work like you expected.
But there can of course be other cases where Groovy and Java are different.
Always remember, the Groovy syntax is close to the Java syntax, yet not identical.
Afair each valid Java code is also valid Groovy code, but not necessarily with the exact same meaning as you can see in this example.