runtime invocation of method with arguments in groovy - groovy

For simplicity let's say I have code similar to this:
def testMethod(String txt) {
return txt;
}
public String evaluate(String expression) {
//String result = "${testMethod('asdasdasd')}";
String result = "${expression}";
return result;
}
I need the expression value which is passed to method "evaluate" to be executed.
in case of calling
// everything works perfectly well,
String result = "${testMethod('samplestring')}";
in case of calling
// (when expression = testMethod) - everything works perfectly well,
String result = "${expression}"("samplestring");
in case of calling
// (when expression = testMethod('samplestring')) - it's not working.
// I see testMethod('samplestring') as the result, but I need it to be evaluated.
String result = "${expression}"
How can I do that?
Thanks.

Thus should work as well;
Eval.me( "${expression}" )
Edit
As pointed out, this won't work as it stands, you need to pass in the script that contains the method with Eval.x like so:
def testMethod(String txt) {
txt
}
public String evaluate(String expression) {
String result = Eval.x( this, "x.${expression}" )
result
}
println evaluate( "testMethod('samplestring')" )
That will print samplestring

You may use the GroovyShell class for this purpose, but you will need to define a Binding AFAIK. This works in the Groovy Console:
def testMethod(txt) {
"$txt!";
}
def evaluate(String expression) {
def binding = new Binding(['testMethod': testMethod])
new GroovyShell(binding).evaluate(expression)
}
evaluate('testMethod("Hello World")');

Related

How do I define an entire object and methods on single line

This throws an error (can't set on null object)
def currentBuild = [:].rawBuild.getCauses = { return 'hudson.model.Cause$UserIdCause#123abc' }
I need to do it on multiple lines like this
def currentBuild = [:]
currentBuild.rawBuild = [:]
currentBuild.rawBuild.getCauses = { return 'hudson.model.Cause$UserIdCause#123abc' }
Is there a terse way to define this object on a single line or statement? I don't understand why my single line attempt doesn't work.
Instead of chaining setters, I'd just use a map literal with the nested
values. E.g.
def currentBuild = [rawBuild: [getCauses: { return 'hudson.model.Cause$UserIdCause#123abc' }]]
println currentBuild.rawBuild.getCauses()
// → hudson.model.Cause$UserIdCause#123abc
If you have to go more imperative instead of declarative, have a look at
.get(key, fallback), .withDefault{ ... }, .tap{ ... }.
BTW: those are not objects but just maps.

Why doesn't this return statement work while the println() function works in Kotlin?

fun main(args: Array<String>) {
CustomerData.count = 98
CustomerData.typeOfCustomers()
println(CustomerData.count)
}
object CustomerData {
var count: Int = -1
fun typeOfCustomers(){
println("This came from typeOfCustomers function in CustomerData class")
}
}
This prints "This came from typeOfCustomers function in CustomerData class" to console. But, when I use return instead of println() that line doesn't return to console like below code
fun main(args: Array<String>) {
CustomerData.count = 98
CustomerData.typeOfCustomers()
println(CustomerData.count)
}
object CustomerData {
var count: Int = -1
fun typeOfCustomers():String{
return "This came from typeOfCustomers function in CustomerData class"
}
}
Return just returns an object from the function. In your case, the object returned is a String. To print the string you would need to do this:
val typeOfCustomer = CustomerData.typeOfCustomers()
println("${CustomerData.count} $typeOfCustomer")
Both your examples call CustomerData.typeOfCustomers().
In the first example, that call writes the string directly to the console, which is why you see it.
In the second example, the call returns the string but does nothing with it, so the string is simply discarded.
Like most modern languages, in Kotlin the result of a function/method call is a value, and a value is always a valid statement; the value will simply be ignored unless you do something with it.
If you instead used that value in a println() call:
println(CustomerData.typeOfCustomers())
…then you'd see your string on the console as expected.  Or you could assign the result to a variable:
val type = CustomerData.typeOfCustomers()
Or use it in an expression:
val message = "Call returned: " + CustomerData.typeOfCustomers()
Or call one of its methods, or use it in any other way.
Otherwise, Kotlin will make the call but simply ignore the result.

AST Transformation to wrap entire method body in a closure

I'm trying to do something rather simple. I would like to wrap the whole method code into an additional closure block that would measure the execution time. Right now I'm getting a really not helpful error message:
Error:Groovyc: NPE while processing Test.groovy
Annotation:
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.METHOD])
#GroovyASTTransformationClass(["WithTimingASTTransformation"])
public #interface WithTiming {
}
My wrapping closure:
class Benchmark {
static def measureTime(Closure cl) {
def start = System.currentTimeMillis()
def result = cl()
def time = System.currentTimeMillis() - start
println "it took $time"
result
}
}
My Transformation:
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class WithTimingASTTransformation implements ASTTransformation {
#Override
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
MethodNode method = astNodes[1]
method.code = wrap(method)
}
private Statement wrap(MethodNode method) {
def newBlock = new BlockStatement()
newBlock.addStatement(
new ExpressionStatement(
new StaticMethodCallExpression(
new ClassNode(Benchmark),
'measureTime',
new ArgumentListExpression(
new ClosureExpression(new Parameter[0], method.code)
))))
newBlock
}
}
I'm really stuck here and don't know how can I debug the problem.
There is an answer on a similar topic (wrapping whole method body into a try/catch block here). This works fine but my case is slightly different.
In my case similar NPE was coming from:
java.lang.NullPointerException
at org.codehaus.groovy.classgen.asm.ClosureWriter.createClosureClass(ClosureWriter.java:194)
at org.codehaus.groovy.classgen.asm.ClosureWriter.getOrAddClosureClass(ClosureWriter.java:159)
at org.codehaus.groovy.classgen.asm.ClosureWriter.writeClosure(ClosureWriter.java:90)
at org.codehaus.groovy.classgen.AsmClassGenerator.visitClosureExpression(AsmClassGenerator.java:673)
Whereas:
if (parameters == null || expression.getVariableScope() == null) {
parameters = Parameter.EMPTY_ARRAY;
} else if (parameters.length == 0) {
// let's create a default 'it' parameter
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
parameters = new Parameter[]{it};
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
if (ref != null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
}
and line 194 (as of https://github.com/groovy/groovy-core/commit/a52d0d3c5dd1cbb342992d36235171718a563c8b) is:
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
Thus you need to define a VariableScope for your ClosureExpression. I had to add tracing into org.codehaus.groovy.ast.ClosureWriter to find this, because there is an issue with exception display on stage of Class Generation - both in IntelliJ Idea and in Groovy Console - it does not show proper lines of code.
Furthermore, I think that either ClosureWriter or ClosureExpression constructor can be fixed to work aligned by default - without this NPE. I will possibly submit an issue to Groovy Jira for this.
Now I am able to inject closure expression in my code. But struggling to call this closure.
Getting:
groovy.lang.MissingMethodException: No signature of method: com.a9ae0b01f0ffc.VSMSGEN.implementation.T_visa_recon_generator$_convert_vts_log_to_ctf_closure2.call() is applicable for argument types: () values: []
Long story short, after some iterations my method-wrapping AST looks like this:
BlockStatement bs = new BlockStatement()
ClosureExpression closureExp = new ClosureExpression( methodNode.parameters, methodNode.code )
closureExp.variableScope = new VariableScope() // <- this does the trick!
bs.addStatement new ExpressionStatement( new StaticMethodCallExpression( new ClassNode( TransactionUtil ), 'wrap', new ArgumentListExpression( closureExp ) ) )
methodNode.code = bs
The line closureExp.variableScope = new VariableScope() avoids the NPE in ClosureWriter.java:194 and the whole thing runs like a charm!
Hope it helps someone...

Is there a way to intercept all method calls in Groovy?

I need to intercept method calls on predefined Java classes. For example, lets say I need to intercept String class split method, how do I do this?
I tried this which works, but I doesn’t want end user to change their code by wrapping their calls in with proxy block.
Is there any way this can be achieved with Groovy?
If what you want to do is intercept a call to a specific method you can do something like this...
// intercept calls to the split method on java.lang.String
String.metaClass.split = { String arg ->
// do whatever you want to do
}
If what you want to do is intercept a call to a specific method and do some stuff in addition to invoking the original (like to wrap the real method with some of your own logic) you can do something like this:
// get a reference to the original method...
def originalSplit = String.metaClass.getMetaMethod('split', [String] as Class[])
// now add your own version of the method to the meta class...
String.metaClass.split = { String arg ->
// do something before invoking the original...
// invoke the original...
def result = originalSplit.invoke(delegate, arg)
// do something after invoking the original...
// return the result of invoking the original
result
}
I hope that helps.
you want to use MetaClass for that see doc
ExpandoMetaClass.enableGlobally()
//call 'enableGlobally' method before adding to supplied class
String.metaClass.split = { regex ->
println "calling split from $delegate with $regex"
delegate.split regex, 22
}
To intercept all method calls in a class override Groovy's invokeMethod. Example:
class Test {}
Test.metaClass.foo = {"foo() called"}
Test.metaClass.static.bar = {"bar() called"}
Test.metaClass.invokeMethod = { name, args ->
handleInterception(name, args, delegate, false)
}
Test.metaClass.static.invokeMethod = { name, args ->
handleInterception(name, args, delegate, true)
}
def handleInterception(name, args, delegate, isStatic) {
def effDelegate = isStatic ? delegate : delegate.class
println ">> Entering ${delegate.class.name}.$name() with args: $args"
def metaMethod = effDelegate.metaClass.getMetaMethod(name, args)
if (!metaMethod) {
println "-- Method not found: $name($args)"
return
}
try {
def result = metaMethod.invoke(delegate, args)
println "<< Leaving ${delegate.class.name}.$name() with result: $result"
return result
} catch (ex) {
println "-- Exception occurred in $name: $ex.message"
throw ex
}
}
new Test().foo("1", 2)
Test.bar(2)
new Test().onTheFly(3)
Code taken from Roshan Dawrani's post at groovyconsole.appspot.com.
Output:
>> Entering Test.foo() with args: [1, 2]
-- Method not found: foo([1, 2])
>> Entering java.lang.Class.bar() with args: [2]
<< Leaving java.lang.Class.bar() with result: bar() called
>> Entering Test.onTheFly() with args: [3]
-- Method not found: onTheFly([3])
Other options:
Custom MetaClass implementing invokeMethod
Implementing the Interceptor Interface. Read more in this tutorial

XmlSlurper: Changing text in element with namespace

I don't understand why xml."con:cred"."ser:user" = "modified_username" doesn't change the text. Can someone explain this?
input = """
<kuk:acc xmlns:kuk="kuk">
<con:cred xmlns:con="http://www.bea.com/wli/sb/resources/config">
<ser:user xmlns:ser="http://www.bea.com/wli/sb/services">username</ser:user>
</con:cred>
</kuk:acc>
"""
def xml = new XmlSlurper(keepWhitespace:true).parseText(input).declareNamespace(
ser:"http://www.bea.com/wli/sb/services",
con:"http://www.bea.com/wli/sb/resources/config")
println xml."con:cred"."ser:user"
xml."con:cred"."ser:user" = "modified_username" // That doesn't work
println xml."con:cred"."ser:user"
xml.cred.user = "modified_username" // That works
println xml."con:cred"."ser:user"
/*
def outputBuilder = new StreamingMarkupBuilder()
String result = outputBuilder.bind{ mkp.yield xml }
println result
*/
I've been digging in this problem some time and was about to ask just the same thing. Given that the method invoked when using the overloaded '=' operator is putAt(int, Object), a closer look into GPathResult code:
public void putAt(final int index, final Object newValue) {
final GPathResult result = (GPathResult)getAt(index);
if (newValue instanceof Closure) {
result.replaceNode((Closure)newValue);
} else {
result.replaceBody(newValue);
}
}
shows that replaceBody should be invoked. As *tim_yates* points out, replaceBody works well, so it seems that replaceNode is invoked instead (I cannot see why). Digging in NodeChildren's replaceNode, we can see that
protected void replaceNode(final Closure newValue) {
final Iterator iter = iterator();
while (iter.hasNext()) {
final NodeChild result = (NodeChild) iter.next();
result.replaceNode(newValue);
}
}
the closure never gets called, so nothing is done when replaceNode is invoked. So I think that there's a bug in replaceNode (it does nothing), and when doing xml."con:cred"."ser:user" = "modified_username" the right part of the expression is evaluated as a Closure (I need help in this point to understand why :-).

Resources