Groovy is so darn cool

I'm using the syntax tree from Proparse+ProRefactor, and the Groovy language, to generate XML data about procedures in an application. I want to generate XML with entries that look like this:

<tableref table='customer' create='true' write='true' delete='true' />

Using a Groovy builder and a few simple Groovy objects , I'm able to make it remarkably simple. Assuming I already have a builder object and I'm getting my data from an object named symbolRefs:

symbolRefs.tableRefMap.each{ name, ref ->
	builder.tableref(ref.properties)
}

That's it! That's what generates the XML entries! (Yes, Groovy Builders really do look like magic.) The builder.tableref bit causes the builder to generate the 'tableref' XML entry. That is a straightforward use of a Groovy Builder. But even if you are familiar with Groovy builders, you might be surprised to see that I'm able to use the properties of an object to generate the attributes within the XML tag. This is pretty darn cool.

See, my 'ref' object is a Groovy Expando object (i.e. an instance of the Expando class from the Groovy library). Its properties can be fetched as a map (a list of key/value pairs), and a map can be used directly as the attributes for an XML tag in the builder. Wow.

Now let's take a look at the code I'm using to generate my data. This all took place beforehand, within the 'symbolRefs' object that I referenced above.

Expando tableref = tableRefMap[tablename]
if (tableref==null) {
	tableref = new Expando()
	tableRefMap[tablename] = tableref
	tableref.table = tablename
}
switch (node.parent().type) {
	case TokenTypes.CREATE:
	case TokenTypes.INSERT:
	case TokenTypes.IMPORT:
		tableref.create = true
		break
	case TokenTypes.DELETE_KW:
		tableref.create = true
		break
	case TokenTypes.UPDATE:
	case TokenTypes.ASSIGN:
		tableref.write = true
		break
	case TokenTypes.RAWTRANSFER:
	case TokenTypes.BUFFERCOPY:
		// If this node is *not* the first RECORD_NAME child, then it's the target.
		if (node.parent().findDirectChild(TokenTypes.RECORD_NAME) != node)
			tableref.write = true
		break
}

This code is executed for every table reference node found in the syntax tree. First it looks up the table name in 'tableRefMap' to see if it already has an entry. If not, it creates a new Expando object and maps it to the table's name. It adds 'table = tablename' as a property of the new Expando object.

Next, the code looks at the parent of the table reference node, to see if that bit of syntax is doing a read, write, or delete of the table record. Based on that it adds a new property to the Expand object. Simple!