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 :-).
Related
Is there a way to skip MissingPropertyException while using GroovyShell.evaluate?
def sharedData = new Binding()
def shell = new GroovyShell(sharedData)
shell.evaluate("a=5; b=1") // works fine
// How to not get MissingPropertyException, or silently ignore property 'a'
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
I know about the Expando solution, is there a way to do it without defining a class?
A very minimal approach would be to override Binding.getVariable.
Note that this is very straight forward: "all exceptions" are ignored - you might want to have better logging or more accurate error handling.
import groovy.lang.*
class NoOpBinding extends Binding {
#Override
Object getVariable(String name) {
try {
return super.getVariable(name)
}
catch (Throwable t) {
println "Ignoring variable=`$name`"
return null
}
}
}
def shell = new GroovyShell(new NoOpBinding())
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
// → Ignoring variable=`a`
println shell.getVariable('b')
// → 1
You could do something like this:
def binding = [:].withDefault { }
def shell = new GroovyShell(binding as Binding)
shell.evaluate 'a; b=1'
I am puzzling over Groovy delegation strategy in nested closures. Here is a simplified example:
class Clazz {
String name
String whoAmI() {
return name
}
void doit(Clazz clazz) {
def cl = {
println "Outer closure: resolveStrategy=${resolveStrategy}, " +
"implicit=${whoAmI()}, delegated=${delegate.whoAmI()}"
{->
println "Inner closure: resolveStrategy=${resolveStrategy}, " +
"implicit=${whoAmI()}, delegated=${delegate.whoAmI()}"
}.call()
}
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl.delegate = clazz
cl()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Output:
Outer closure: resolveStrategy=1, implicit=B, delegated=B
Inner closure: resolveStrategy=0, implicit=A, delegated=B
Why doesn't resolveStrategy propagate to the inner closure? The point of setting resolution strategy is to change the way implicit this is resolved. But if it doesn't propagate inside closures, then the mechanism seems to be as good as useless. Closures are so ubiquitous in Groovy that you can hardly write a couple of lines without them.
Found a similar question from almost five years ago: Nested closure resolution different between methods and properties? Apparently it's a bug, and it has been open without any movement since then: https://issues.apache.org/jira/browse/GROOVY-7232
The linked post notes something I also noticed: properties are correctly resolved to the delegate, but not method calls. Here is a reworked example to demonstrate this:
class Clazz {
String name
void doit(Clazz clazz) {
def cl = {
println "Outer closure: property=${name}, method=${getName()}"
{->
println "Inner closure: property=${name}, method=${getName()}"
}.call()
}
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl.delegate = clazz
cl()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Result:
Outer closure: property=B, method=B
Inner closure: property=B, method=A
Someone in the linked post posted a workaround using rehydrate. In my case though it works (sort of) only if all references are replaced, including this!
class Clazz {
String name
void doit(Clazz clazz) {
def cl = {
println "Outer closure: property=${name}, method=${getName()}"
{->
println "Inner closure: property=${name}, method=${getName()}"
}.call()
}
cl.rehydrate(clazz, clazz, clazz)()
}
}
def a = new Clazz(name: 'A')
def b = new Clazz(name: 'B')
a.doit(b)
Result:
Outer closure: property=B, method=B
Inner closure: property=B, method=B
The original question was deleted before I could follow up with a further answer it, so I'm reposting the question with the answer to follow:
I am unable to modify my method using AST Transformation as I cannot figure out how to execute previous method statements after my modification. I extract the statements from the method, save it in some temporary variable, but later, after my modification, when I try to execute it I get MissingPropertyException: No such property: code for class: Calculator as like I am trying to use a property from my class and not the previous code block from my method. Any ideas what am I doing wrong?
//annotation
#Retention(RetentionPolicy.SOURCE)
#Target([ElementType.TYPE])
#GroovyASTTransformationClass("CreatedAtTransformation")
public #interface CreatedAt {
String name() default "";
}
//AST Transformation
#GroovyASTTransformation(phase = SEMANTIC_ANALYSIS)
public class CreatedAtTransformation implements ASTTransformation {
public void visit(ASTNode[] astNodes, SourceUnit source) {
//private final long field creation
ClassNode myClass = (ClassNode) astNodes[1]
ClassNode longClass = new ClassNode(Long.class)
FieldNode field = new FieldNode("timeOfInstantiation", FieldNode.ACC_PRIVATE | FieldNode.ACC_FINAL, longClass, myClass, new ConstantExpression(System.currentTimeMillis()))
myClass.addField(field)
//statement
AstBuilder ab = new AstBuilder()
List<ASTNode> statement = ab.buildFromCode {
timeOfInstantiation
}
//value of the annotation expression(name of the method)
def annotationExpression = astNodes[0].members.name
String annotationValueString = annotationExpression.value
//public final method creation
myClass.addMethod(annotationValueString, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, ClassHelper.Long_TYPE,[] as Parameter[], [] as ClassNode[], statement[0])
//modification of method "add"
def addMethods = myClass.getMethods("add")
for(m in addMethods){
def code = m.getCode().statements
//statement
AstBuilder abc = new AstBuilder()
List<ASTNode> statement1 = abc.buildFromCode {
timeOfInstantiation = System.currentTimeMillis()
for(c in code){
c.expression
}
}
m.setCode(statement1[0])
}
//modification of method "subtract"
def subtractMethods = myClass.getMethods("subtract")
for(m in subtractMethods){
def code = m.getCode().statements
//statement
AstBuilder abc = new AstBuilder()
List<ASTNode> statement1 = abc.buildFromCode {
timeOfInstantiation = System.currentTimeMillis()
for(c in code){
c.expression
}
}
m.setCode(statement1[0])
}
}
}
//class
final calculator = new GroovyShell(this.class.getClassLoader()).evaluate('''
#CreatedAt(name = "timestamp")
class Calculator {
int sum = 0
def add(int value) {
int v = sum + value
sum = v
}
def subtract(int value) {
sum -= value
}
}
new Calculator()
''')
//test
assert System.currentTimeMillis() >= calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()
def oldTimeStamp = calculator.timestamp()
sleep(1000)
calculator.add(10)
assert oldTimeStamp < calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()
oldTimeStamp = calculator.timestamp()
sleep(1000)
calculator.subtract(1)
assert oldTimeStamp < calculator.timestamp()
assert calculator.timestamp() == calculator.timestamp()
println 'well done'
There is more code than is actually needed for the question. Important part are those modifications of methods. Thanks in advance.
From my perspective I don't know if the code from the AST is actually useful or just to get and example working, to learn AST Transforms...
So my original answer was:
It can be hard to get help when it comes to AST transforms. While it is a guess I'm thinking it might have to do with variable scope. There is a VariableScopeVisitor that is run early in the AST process, setting the scoping of variables, however from the description of your AST you are adding code that you want to access later. So you may have to run the VariableScopeVisitor again, to fix it so that the existing code has access to your injected code.
I did an intro AST talk at GR8Conf.US this year, which has a lot of resources:
https://docs.google.com/presentation/d/1D4B0YQd0_0HYxK2FOt3xILM9XIymh-G-jh1TbQldbVA/edit?usp=sharing
I would take a look at this article that talks about variable scope:
However the real answer is that
AST Transforms can be difficult, and using the AstBuilder while it can be convience can introduce issues, so I often use the API directly. Once I learn Macros and Macro Methods, new in Groovy 2.5 I might not have to use the API as much, but until then I rewrote part of the code using the API like this:
//modification of method "add"
def addMethods = myClass.getMethods("add")
for(m in addMethods){
def code = m.getCode().statements
//statement
//AstBuilder abc = new AstBuilder()
Statement s1 = new ExpressionStatement(
new BinaryExpression(
new VariableExpression('timeOfInstantiation'),
Token.newSymbol(org.codehaus.groovy.syntax.Types.EQUAL,0,0),
new MethodCallExpression(
new ClassExpression(new ClassNode(java.lang.System)),
'currentTimeMillis',
ArgumentListExpression.EMPTY_ARGUMENTS
)
)
)
// List<ASTNode> statement1 = abc.buildFromString('timeOfInstantiation = System.currentTimeMillis()')
// List<ASTNode> statement1 = abc.buildFromCode {
// timeOfInstantiation = System.currentTimeMillis()
// for(c in code){
// c.expression
// }
// }
code.add(0,s1)
//m.setCode(statement1[0])
}
This code can be cleaned up a bit, but it should work. I also had to change the timeOfInstantiation to be private, instead of final, so that the assignment code would work like this:
FieldNode field = new FieldNode("timeOfInstantiation", FieldNode.ACC_PRIVATE, longClass, myClass, new ConstantExpression(System.currentTimeMillis()))
I would also look into the test application reference in my presentation, as it will allow for debugging AST Transforms and using the Groovy Console to see what the transforms are doing.
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...
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")');