Ant transforms using Groovy template scriptdef

Ant provides powerful transformation capability. For example, the copy task can include filters that replace tokens in the text. There is also the XSLT task to perform more complex XML based transforms.

Sometimes you may need more dynamic or procedural transforms for non-xml based templates. While this can be done in Ant, they should not be; Ant should really only be used in a declarative manner. One can do this outside of Ant using custom solutions or use the various templating frameworks, such as Velocity and Freemarker, which provide Ant plugins.

Another alternative is the Groovy language which has always provided text processing in the form of GStrings and templates. These are used in various solutions such as Groovlets and Groovy Server Pages. Below I show how Groovy templates can be easily used in Ant.

listing 1 shows a meeting agenda text template with a list of topics. Note that it has a GString interpolated string using “$” character. It also, analogous to Java Server Pages, uses a scriptlet with <% and %> to execute code. Yes I know, scriptlets are evil.

List 1, template1.txt, a template:
[sourcecode]
Hello ${name}!
Agenda is:
<%
subject.split(‘,’).each{
%>- $it
<% }
%>
[/sourcecode]

In listing 2 below, an Ant script invokes the “template” task to transform the template in listing 1.

Listing 2, build.xml, use template task:
[sourcecode language=”xml”]
<project default="init">

<import file="tasks.xml"/>

<target name="init"
xmlns:jb="http://octodecillion.com">
<jb:template
src="template1.txt"
dest="temp.txt"
property="result"
failOnError="true">

<property name="name"
value="world"/>
<property name="subject"
value="beans,GStrings,templates"/>

</jb:template>

<echo>${result}</echo>

</target>
</project>
[/sourcecode]

Listing 3 shows an example run.

Listing 3, example use:
[sourcecode]
init:
Saving result to temp.txt
[echo] Hello world!
[echo] Agenda is:
[echo] – beans
[echo] – GStrings
[echo] – templates
[echo]

BUILD SUCCESSFUL
[/sourcecode]

Listing 4 is the implementation. It is a simple scriptdef using Groovy as the script language. The Groovy code is inline but could have been in external file using the “src” attribute of the scriptdef declaration.

Listing 4, tasks.xml, the scriptdef implementation:
[sourcecode language=”groovy”]
<project>
<!– config stuff not shown –>

<!–
Render a Groovy template using properties to create the binding
–>
<scriptdef name="template"
language="Groovy"
classpathref="groovy.lib"
uri="http://octodecillion.com">

<attribute name="src"/> <!– required –>
<attribute name="dest"/> <!– optional –>
<attribute name="property"/> <!– optional –>
<attribute name="failOnError"/> <!– optional, true –>
<element name="property" type="property"/> <!– required –>

<![CDATA[
def SRC= ‘src’;
def PROPERTY = ‘property’
def DEST = ‘dest’
def FAILONERROR = ‘failonerror’

try{
// convert element properties to map
def map = [:]
elements.get(PROPERTY).each{
map.put(it.name,it.value)
}

// render the template
String result = new groovy.text.GStringTemplateEngine()
.createTemplate(new File(attributes.get(SRC))
.getText())
.make(map).toString()

def prop = attributes.get(PROPERTY)
if(prop){ // save to property?
project.setNewProperty(prop, result)
}

def dest = attributes.get(DEST)
if(dest){ // write to file?
project.log("Saving to $dest",project.MSG_INFO)
new File(dest).write(result)
}
}catch(Exception ex){
def f = attributes.get(FAILONERROR)
def failOnError = f ? f : true;

if(failOnError == ‘true’){
throw ex;
}
}
]]>
</scriptdef> <!– end name="template" –>

</project>
[/sourcecode]

Summary
Shown was an example of using Groovy templates in an Ant task. A simple scriptdef was created to allow reuse in Ant scripts.

Extensions
Kind of begs the question, since scriptlets have a bad rep, should there instead be a way of using taglibs in the templates. I think Grails implemented Groovy taglibs.

References

Leave a Reply

Your email address will not be published. Required fields are marked *