Wednesday, June 22, 2011

groovy - parsing reading builder content from an external file

we all know that groovy builders are great and fun and can make your life so much easier.

For example if you want to create a simple xml file in your groovy script you can just do the following


def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.records() {
car(name:'HSV Maloo', make:'Holden', year:2006) {
country('Australia')
record(type:'speed', 'Production Pickup Truck with speed of 271kph')
}
car(name:'P50', make:'Peel', year:1962) {
country('Isle of Man')
record(type:'size', 'Smallest Street-Legal Car at 99cm wide and 59 kg in weight')
}
car(name:'Royale', make:'Bugatti', year:1931) {
country('France')
record(type:'price', 'Most Valuable Car at $15 million')
}
}



looks easy and is easy. But now you come to the point where you think. Mhm it would be nice, if I could just read the builder code from an external file and generate the xml on the fly using this. For example you have to generate an xml configuration file and rather simplify this process using groovy and this has to be done more than once...

After a couple of hours googleing the best solution I came up with, was to provide your own binding to the builder, which simplified things quite a bit.



class XmlBinding extends Binding{
def builder


Object getVariable(String name) {
return { Object... args -> builder.invokeMethod(name,args) }
}
}


let's assume we like to read the following builder code, defined in a file named 'test.groovy'



config{
bin{
allow(minimumClassSize:1)
}
}



which all you need to do, now you could for example have a main method somewhere containing this:


def groovyScriptContent = new File("test.groovy").text
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
def binding = new XmlBinding()
binding.builder = xml

def dslScript = new GroovyShell().parse(groovyScriptContent)
dslScript.binding = binding
dslScript.run()

println writer.toString()


and as a result it generates the following xml document


<config>
<bin>
<allow minimumClassSize='1' />
</bin>
</config>