Strange behaviour of adding dynamic methods on Groovy's NodeChild - groovy

I'm using Grails XML Parser to parse an XML string and after getting the parsed NodeChild instance, I'm adding dynamic methods on that instance like below:
import grails.converters.XML
import groovy.util.slurpersupport.NodeChild
NodeChild result = XML.parse("<root></root>")
result.getMetaClass().methodA = { return "a" }
result.getMetaClass().methodB = { return "b" }
println rootNode.methodA()
println rootNode.methodB()
Now the line where I'm calling methodA() and expecting "a" to be printed, I'm getting MissingMethodException that methodA() not found.
I investigated on this for some time and found that the all dynamic methods getting replaced with the last dynamic method we add, i.e. in this case: methodB() is replacing (or doing something) methodA(), so I call & print methodB() first, it prints "b" properly.
This strikes me to another test as following:
import grails.converters.XML
import groovy.util.slurpersupport.NodeChild
String result = "any-other-data-type-instance-here-to-inject-dynamic-methods"
result.getMetaClass().methodA = { return "a" }
result.getMetaClass().methodB = { return "b" }
println rootNode.methodA()
println rootNode.methodB()
In this case, both statement prints fine. So the problem is only with the classNodeChild. I'm using exando metaclass feature for long time and I faced such kind of problem. Any idea, that why this is happening?

You need to assign the metaclass methods before the instance is returned, otherwise it will have the old metaclass and not the new one with the new methods. Also, assign to the class, not the instance - I'm not sure if you wanted to only affect this instance's metaclass but that's not the syntax.
This works:
import grails.converters.XML
import groovy.util.slurpersupport.NodeChild
NodeChild.metaClass.methodA = { return "a" }
NodeChild.metaClass.methodB = { return "b" }
NodeChild result = XML.parse("<root></root>")
println result.methodA()
println result.methodB()
Note that you're defining methods with an implicit it argument, but passing nothing, so Groovy passes a null. If you intend for the methods to have no arguments, use this syntax:
NodeChild.metaClass.methodA = { -> return "a" }
NodeChild.metaClass.methodB = { -> return "b" }

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.

How can I rectify static scope error when referencing a static variable in my groovy script?

I have a script which exports excel worksheets to csv, and I am attempting to escape commas, quotes and the like. I have created a static method that encodes the value of the cell being accessed. This method references a static variable which stores a Pattern.compile() value.
I have tried using def rxquote within the method but this gives me a different error stating that using static modifier before declaring my rxquote variable is illegal. Code is below followed by error message.
#!/usr/bin/env groovy
#Grab(group = 'org.apache.poi', module = 'poi', version = '4.1.0')
#Grab(group = 'org.apache.poi', module = 'poi-ooxml', version = '4.1.0')
import java.util.regex.*
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.apache.poi.ss.usermodel.*
static Pattern rxquote = Pattern.compile("\"")
static private String encodeValue(String value) {
boolean needQuotes = false;
if ( value.indexOf(',') != -1 || value.indexOf('"') != -1 ||
value.indexOf('\n') != -1 || value.indexOf('\r') != -1 ){
needQuotes = true;
}
Matcher m = rxquote.matcher(value)
if ( m.find() ) {
needQuotes = true
value = m.replaceAll("\"\"")
}
if ( needQuotes ) {
return "\"" + value + "\""
}
else return value;
}
//for(){
// ... export csv code (which works on its own)
//}
Error message on compile:
Apparent variable 'rxquote' was found in a static scope but doesn't refer to a local variable, static field or class. Possible causes:
You attempted to reference a variable in the binding or an instance variable from a static context.
You misspelled a classname or statically imported field. Please check the spelling.
You attempted to use a method 'rxquote' but left out brackets in a place not allowed by the grammar.
# line 27, column 17.
Matcher m = rxquote.matcher(value);
^
I've tried researching the issue and have found several similar questions here, but none of the solutions appear to apply to this situation as far as I can tell. I expected a static declaration of the variable to avoid this problem, but it seems there's something I'm missing.
you can't declare static variable in groovy script.
it's allowed only in groovy/java class.
error does not correspond to situation.
should be : Modifier 'static' not allowed here.
as workaround for static variables you can use some class:
class Const{
static String bar = 'test'
}
static private String foo() {
return Const.bar
}
foo()

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...

runtime invocation of method with arguments in 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")');

How to iterate over groovy class non-static closures and optionally replace them?

I'd like to iterate over groovy class non-static closures and optionally replace them.
I can get MetaClass with something like
MyClassName.metaClass
and from there I can get all properties like
metaClassObject.properties
which is the list of MetaProperty objects.
The problem is that I can't detect which of those properties are closures and which are simple objects. MetaProperty object's type property return Object in both case.
And about replacing: Let's say, I know that it is a closure A, then can I create another closure B that wraps closure A with some optional code and replace that closure A with B in the class definition? Should work like some sort of interceptor.
This is one way I have tried out:
class Test {
def name = 'tim'
def processor = { str ->
"Hello $name $str"
}
}
Test t = new Test()
t.metaClass.properties.each {
if( t[ it.name ].metaClass.respondsTo( it, 'doCall' ) ) {
println "$it.name is a closure"
def old = t[ it.name ]
t.metaClass[ it.name ] = { str ->
"WOO! ${old( str )}"
}
}
}
println t.processor( 'groovy!' ) // prints 'WOO! Hello tim groovy!'
However, it would need expanding as I rely on the fact that I know how many parameters it takes for the patching closure replacement
There may also be a simpler way to do this...

Resources