Tag Archives: templates

Use Groovy to recurse folders and create files via template

Via scripting one can create multiple files in a folder hierarchy and customize the file contents based on the folder location. This can be easily done using the Groovy language.

Note: In the past “package.html” files were used, but the latest approach is to create “package-info.java” files. These Java files contain the package Javadoc info, and have other uses. Kind of the return of Java classes as configuration as used in the bad ole complex early EJB days.

In this particular use case, I have a legacy Java project that does not contain package level Javadoc files. There are about a hundred package folders. It would be daunting to create these manually. It is not a mere copy since the package info must contain the specific package declaration: package x.y.z;.

 

In listing 1 below, the processFolders method takes in a closure “createInfo” and a variable length argument of Strings. The strings are to allow creating the files starting at multiple base folders. With the paths.each{} we take each string and create a File object. Now we recurse all folders at this file location. At each folder in the recursion we invoke the closure.

import groovy.io.FileType

def processFolders(createInfo, String... paths) {
    paths.each{
        def base = it
        new File(it).eachFileRecurse(FileType.DIRECTORIES){ file -> 
             def path = file.getPath()
             println "$path"
             createInfo(base, path)                                
        }
   }
}

def templateText = ''' 
/**
 * TODO Summary.
 * <p>
 * TODO Description.
 */
package ${pack};
'''

def template = new groovy.text.SimpleTemplateEngine()
       .createTemplate(templateText)

processFolders({base, p ->
    def pack = (p-base).replace(File.separator, '.')
          .replaceFirst(/^\.+/, '');
    new File(p,"package-info.java").text = template.make(['pack':pack]) 
}, 'src\\main\\java','src\\test\\java')

Listing 1
 

Because we are using variable length arguments, which must be the last parameter in a method, we can’t use the usual Groovy closure idiom, and define it inline (but maybe there is a way?):

processFolders(){
   ... body of closure ...
}

Alternatively, we could have used a named closure instead of inline, as follows:

def proc = {base, p ->
    def pack = (p-base).replace(File.separator, '.').replaceFirst(/^\.+/, '');
    new File(p,"package-info.java").text = template.make(['pack':pack]) 
}

At each folder during recurse, we keep the base folder, then later we subtract the folder paths (yup, Groovy allows String subtraction), and convert the folder path into a package string. Then we resolve the template with the binding containing the package name. The String produced by the template is written to a new file via the file’s ‘text’ property.

The result when run is the creation of package-info.java files at each subfolder, and the package line will be correct. Each file will have two TODO entries for the summary and description. Of course, the same code with minor changes can be used to create package.html files instead. Or just add a switch for ‘old’ or ‘new’ package level info file.

Updates
March 4, 2014: Changed to use eachDirRecurse. Not tested yet.
March 5, 2014: Hmmmm. eachDirRecurse didn’t work.

Environment

  • Groovy Version: 2.2.2 JVM: 1.7.0_25 Vendor: Oracle Corporation OS: Windows 7
  • Eclipse 4.3
  • Groovy-Eclipse Feature: 2.9.0.xx-20140228-1800-e43-SNAPSHOT

Some links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

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:

Hello ${name}!
Agenda is:
<% 
   subject.split(',').each{
%>- $it 
<% }	
%>

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:

<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>

Listing 3 shows an example run.

Listing 3, example use:

init:
Saving result to temp.txt
     [echo] Hello world!
     [echo]     Agenda is:
     [echo]     - beans
     [echo]     - GStrings
     [echo]     - templates
     [echo]

BUILD SUCCESSFUL

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:

<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>

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

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.