Using a String as code with Groovy XML Parser - groovy

I am new to groovy - I am hoping this is a simple thing to solve. I am reading in an xml document, and then I am able to access data like this:
def root = new XmlParser().parseText(xmlString)
println root.foo.bar.text()
What I would like to do, is to have loaded the "foo.bar" portion of the path from a file or data base, so that I can do something like this:
def paths = ["foo.bar","tashiStation.powerConverter"] // defined for this example
paths.each {
path ->
println path + "\t" + root.path.text()
}
Obviously the code as written does not work... I thought maybe this would work:
paths.each {
path ->
println path + "\t" + root."${path}".text()
}
...but it doesn't. I based my initial solution on pg 153 of Groovy for DSL where dynamic methods can be created in a similar way.
Thoughts? The ideal solution will not add significant amounts of code and will not add any additional library dependencies. I can always fall back to doing this stuff in Java with JDOM but I was hoping for an elegant groovy solution.

This is very similar to this question from 3 days ago and this question
You basically need to split your path on . and then walk down this list moving through your object graph
def val = path.split( /\./ ).inject( root ) { obj, node -> obj?."$node" }?.text()
println "$path\t$val"
Should do it in this instance :-)

Related

Declare multiple variables in one line in Spock when: block

I would like to know how I can define, without initializing, multiple variables in one line in Spock spec as shown below.
I have tried:
import spock.lang.Specification
class exampleSpec extends Specification {
def "foo"() {
when:
def a, b
a = 0
b = 1
then:
a != b
}
}
But this fails when accessing b:
No such property: b for class: ....
I have managed to get the following to work:
def (a, b) = []
But I'd like something better.
Any help is greatly appreciated!
I am using:
Groovy Version: 2.4.3 JVM: 1.8.0_45 Vendor: Oracle Corporation OS: Linux
I fear that cannot be done in any of the blocks (like given:, setup:, when:). There is an easy work around.
#Grab(group='org.spockframework', module='spock-core', version='0.7-groovy-2.0')
import spock.lang.Specification
class ExampleSpec extends Specification {
def "foo"() {
def a, b
when:
a = 0
b = 1
then:
a != b
}
}
Move the declaration out of when:/given:/setup: block and have them as method variables. The setup:/given: label is optional and may be omitted, resulting in an implicit setup block.
Although this works, I would like to know the reason why this did not work otherwise. You can create an issue in github.
You should use the where: block to pass parameters. There's no def required because they're passed as an argument, if you're feeling fancy you can even have a test that is run multiple times.
def "Use where to pass test data"(){
expect: 1 == myNumber
2 == myOther1
where: myNumber = 1
myOther1 = 2
}
Here's a link to some other examples I've written that show how to pass data to your tests. If you really like multiple assignment (even though it's not a good idea) here's how you could use it in a where: block.
If you're curious about the various blocks here is a summary of everything I've read. Don't take my word for it, here's some official documentation.
You might be happy to know that this issue has been raised already, but it hasn't seen any activity in the last month. My assumption is that something about multiple assignment doesn't agree with the AST transformations that Spock uses on your code.

Unexpected results with groovy.util.slurpersupport.NodeChild.appendNode() (Groovy 2.2.1)

I think what I am trying to do is simple: Add child nodes dynamically to a node (without even knowing the name of the node to be added - developing some framework) using XmlSlurper.
For ease of explaining, something like this:
def colorsNode = new XmlSlurper().parseText("""
<colors>
<color>red</color>
<color>green</color>
</colors>""")
NodeChild blueNode = new XmlSlurper().parseText("<color>blue</color>") // just for illustration. The actual contents are all dynamic
colorsNode.appendNode(blueNode) // In real life, I need to be be able to take in any node and append to a given node as child.
I was expecting the resulting node to be the same as slurping the following:
“””
<colors>
<color>red</color>
<color>green</color>
<color>blue</color>
</colors>"""
However the result of appending is:
colorsNode
.node
.children => LinkedList[Node('red') -> Node('green') -> <b>NodeChild</b>(.node='blue')]
In other words, what gets appended to the LinkedList is the NodeChild that wraps the new node, not the node itself.
Not surprising, looking at the source code for NodeChild.java:
protected void appendNode(final Object newValue) {
this.node.appendNode(newValue, this);
}
Well, I would gladly modify my code into:
colorsNode.appendNode(blueNode<b>.node</b>)
Unfortunately NodeChild.node is private :(, don't know why! What would be a decent way of achieving what I am trying? I couldn’t see any solutions online.
I was able to complete my prototyping work by tweaking Groovy source and exposing NodeChild.node, but now need to find a proper solution.
Any help would be appreciated.
Thanks,
Aby Mathew
It would be easier if you use XmlParser:
#Grab('xmlunit:xmlunit:1.1')
import org.custommonkey.xmlunit.Diff
import org.custommonkey.xmlunit.XMLUnit
def xml = """
<colors>
<color>red</color>
<color>green</color>
</colors>
"""
def expectedResult = """
<colors>
<color>red</color>
<color>green</color>
<color>blue</color>
</colors>
"""
def root = new XmlParser().parseText(xml)
root.appendNode("color", "blue")
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(root)
def result = writer.toString()
XMLUnit.setIgnoreWhitespace(true)
def xmlDiff = new Diff(result, expectedResult)
assert xmlDiff.identical()

GStringImpl in groovy File() constructor

I am running into a bit of a strange error here when creating a new file object with a GStringImpl. If I create a new File (and then list files in that path) with a GStringImpl, I get an empty array, and no error, however if I just a normal string, I get a list of files... While that makes sense in a way I would think that there would be an error somewhere.
Example:
def thisIsAListOfFiles = new File("/absolute/fs/mount/point").listFiles()
def gString = "${StaticClass.propertyStringThatIsAnAbsoluteFilePath}"
def notAListOfFiles = new File(gString).listFiles()
Any thoughts on what is going on here? Is this the expected behavior?
More info:
Groovy Version: 2.1.3
Grails version: 2.2.2 (this is within a grails app of course)
Java version: OpenJDK Runtime Environment (IcedTea 2.3.9)
I start out with a properties file with a bunch of properties like this
com.mycompany.property = "/absolute/directory/path"
Because I cant easily inject grailsApplication into non grails classes (anything in /src/groovy for instance) I inject grailsApplication into bootstrap, and use groovy config slurper to read the properties file from the classpath and set then as static string values in a groovy class Config.groovy. That groovy class then has the static variables of all of the properties I need anywhere within the application.
Note: this is not an issue reading the properties files, or anything along those lines. I log the Config.filePathProperty right before the new File(var).listFiles() occurs and that value is set properly.
I'm pretty sure your static path is set incorrectly. I ran the following code as a test:
String path = '/etc/'
print "String ($path): "
println(new File(path).listFiles().size())
def gpath = "${path}"
print "GString ($gpath): "
println(new File(gpath).listFiles().size())
class Foo {
static String path = '/etc/'
}
print "GString static ($Foo.path): "
println(new File("${Foo.path}").listFiles().size())
And got this result (obviously your file count will vary):
String (/etc/): 122
GString (/etc/): 122
GString static (/etc/): 122
The only time I saw a null result was when the path was invalid, for example:
assert new File("does-not-exist").listFiles() == null
One thing you could do would be to eliminate the GString, which is unnecessary in your example:
def notAListOfFiles = new File(StaticClass.propertyStringThatIsAnAbsoluteFilePath).listFiles()
But I'm sure you'll find a typo in the variable or file path, or another similar issue.

Exhaustively walking the AST tree in Groovy

This is related to my question on intercepting all accesses to a field in a given class, rather than just those done in a manner consistent with Groovy 'property' style accesses. You can view that here: intercepting LOCAL property access in groovy.
One way I've found that will definitely resolve my issue there is to use AST at compile time re-write any non-property accesses with property accesses. For example, a if a class looks like this:
class Foo {
def x = 1
def getter() {
x
}
def getProperty(String name) {
this."$name" ++
}
}
foo = new Foo()
assert foo.getter() == 1
assert foo.x == 2
These assert statements will work out because the getter method access x directly and the foo.x goes through getProperty("x") which increments x before returning.
After some trial and error I can use an AST transformation to change the behavior of the code such that the expression 'x' in the 'getter' method is actually accessed as a Property rather than as a local field. So far so good!
Now, how do I go about getting to ALL accesses of local fields in a given class? I've been combing the internet looking for an AST tree walker helper of some kind but haven't found one. Do I really need to implement an expression walker for all 38 expression types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/expr/package-summary.html and all 18 statement types here http://groovy.codehaus.org/api/org/codehaus/groovy/ast/stmt/package-summary.html? That seems like something that someone must have already written (since it would be integral to building an AST tree in the first place) but I can't seem to find it.
Glenn
You are looking for some sort of visitor. Groovy has a few (weakly documented) visitors defined that you could use. I don't have the exact answer for your problem, but I can provide you a few directions.
The snippet below shows how to transverse the AST of a class and print all method names:
class TypeSystemUsageVisitor extends ClassCodeVisitorSupport {
#Override
public void visitExpression(MethodNode node) {
super.visitMethod(node)
println node.name
}
#Override
protected SourceUnit getSourceUnit() {
// I don't know ho I should implement this, but it makes no difference
return null;
}
}
And this is how I am using the visitor defined above
def visitor = new TypeSystemUsageVisitor()
def sourceFile = new File("path/to/Class.groovy")
def ast = new AstBuilder().buildFromString(CompilePhase.CONVERSION, false, sourceFile.text).find { it.class == ClassNode.class }
ast.visitContents(visitor)
Visitors take care of transversing the tree for you. They have visit* methods that you can override and do whatever you want with them. I believe the appropriate visitor for your problem is CodeVisitorSupport, which has a visitVariableExpression method.
I recommend you to read the code of the AST Browser that comes along with groovyConsole for more examples on how to use Groovy AST Visitors. Also, take a look at the api doc for CodeVisitorSupport.

Groovy way to dynamically invoke a static method

I know in Groovy you can invoke a method on a class/object using a string. For example:
Foo."get"(1)
/* or */
String meth = "get"
Foo."$meth"(1)
Is there a way to do this with the class? I have the name of the class as a string and would like to be able to dynamically invoke that class. For example, looking to do something like:
String clazz = "Foo"
"$clazz".get(1)
I think I'm missing something really obvious, just am not able to figure it out.
As suggested by Guillaume Laforge on Groovy ML,
("Foo" as Class).get(i)
would give the same result.
I've tested with this code:
def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
Try this:
def cl = Class.forName("org.package.Foo")
cl.get(1)
A little bit longer but should work.
If you want to create "switch"-like code for static methods, I suggest to instantiate the classes (even if they have only static methods) and save the instances in a map. You can then use
map[name].get(1)
to select one of them.
[EDIT] "$name" is a GString and as such a valid statement. "$name".foo() means "call the method foo() of the class GString.
[EDIT2] When using a web container (like Grails), you have to specify the classloader. There are two options:
Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader)
or
Class.forName("com.acme.MyClass", true, getClass().classLoader)
The first option will work only in a web context, the second approach also works for unit tests. It depends on the fact that you can usually use the same classloader as the class which invokes forName().
If you have problems, then use the first option and set the contextClassLoader in your unit test:
def orig = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = getClass().classLoader
... test ...
} finally {
Thread.currentThread().contextClassLoader = orig
}
An augmentation to Chanwit's answer illustrating creation of an instance:
def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
Here's another way
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def target = application.domainClasses.find{it.name == 'ClassName'}
target.clazz.invokeMethod("Method",args)
With this you don't need to specify the package name. Be careful though if you have the same class name in two different packages.
Melix on Groovy ML pointed me in the "right" direction on dynamic class method invokation awhile back, quite useful:
// define in script (not object) scope
def loader = this.getClass().getClassLoader()
// place this in some MetaUtils class, invoked on app startup
String.metaClass.toClass = {
def classPath = getPath(delegate) // your method logic to determine 'path.to.class'
Class.forName(classPath, true, this.loader)
}
// then, anywhere in your app
"Foo".toClass().bar()
You could create another string metaClass method to create instances as well, refactoring as appropriate:
String.metaClass.toObject = {
def classPath = getPath(delegate)
Class.forName(classPath, true, this.loader).newInstance()
}
Groovy is pure fun ;--)
I'm running version 1.8.8 groovy... and the simple example works.
Import my.Foo
def myFx="myMethodToCall"
def myArg = 12
Foo."$myFx"(myArg)
Calls Foo.myMethodToCall(12) as expected and desired. I don't know if this has always been the case though.

Resources