The Kaptain on … stuff

19 Apr, 2009

Groovy and Glazed Lists with Grape

Posted by: TheKaptain In: Development

So today while I was cleaning out the garage, I finally got a chance to the listen to the Java Posse podcast on Google Collections and GlazedLists. Google Collections has come in handy a few times, the MultiMap extension most notably. It solves essentially the same kind of problem that I described way back in this little Groovy post.

And recently I had read an article about GlazedLists over on Andres’ blog, so it was great to get some more context around them, especially on how they can greatly simplify development of responsive and scalable Swing tables. It inspired me enough to visit their developer tutorials page and spend an hour watching screencasts. The examples are clear and concise, and the developer, James Lemieux, does a fantastic job of explaining how the different pieces in the GlazedList architecture fit together. If you have a spare hour, well worth watching.

So in the spirit of my last post, it occurred to me how to put this to use on the command line as an example. I chose to do a quick and dirty script for sorting a subset of the results from an ‘svn st’ command run in the current directory. Not terribly useful in its present form, but I think it shows that with a little more effort, you can add some handy utility methods when consuming command line output – namely sorting.

Essentially this is a demonstration of creating a table binding dynamically and baking automatic sorting across multiple columns right into the model. Grape is used to incorporate the GlazedLists functionality, but otherwise all of the dependencies required are available in Groovy 1.6.1. So you can run this directly on the command line of a *nix machine(execute permissions required of course) with very little additional setup. I did have an initial problem with Grape being unable to resolve the GlazedList dependency, but through the magic of Google I found this article which explains how to configure repositories using a grapeConfig.xml file and the problem went away. With no further ado, here’s the entire code.

#!/usr/bin/env groovy
import ca.odell.glazedlists.*
import ca.odell.glazedlists.gui.*
import ca.odell.glazedlists.swing.*
import groovy.swing.*
import javax.swing.*

@Grab (group = 'net.java.dev.glazedlists', module = 'glazedlists_java15', version = '1.8.0')
public class SVNGlazedListExample
{
	public static void main(args)
	{
		def columnNames = ['Code', 'Revision', 'Filename']
		def sortedRevisions

		//execute on the command line and populate buffers with the output
		def sout = new StringBuffer()
		def serr = new StringBuffer()
		def Process process = "svn stat -u".execute([], new File('.'))
		process.consumeProcessOutput(sout, serr)
		process.waitFor()

		SwingBuilder builder = new SwingBuilder();

		/**
		 * closure returns a JComponent showing the buffered output
		 */
		def getTableModel = {out, err ->
			if (err)      //Groovy truth FTW
			{
				//create error output table
				return builder.textPane(size: [500, 500], text: err)
			}
			else
			{
				//create three column table
				def lines = out.toString().split('\n')
				def data = []
				lines.each() {
					def tokens = it.tokenize()  //Groovy add which abstract away spaces vs tabs
					//a hack to be sure, but let's just get trebles for now
					if (tokens.size() == 3)
					{
						//rows in the data are just a map
						data << [code: tokens[0], revision: tokens[1], filename: tokens[2]]
					}
				}
				//passing in null gives us default String comparator
				sortedRevisions = new SortedList(new BasicEventList(data),null)

				//this little trick courtesy of Andres Almiray - Thanks!
				final EventTableModel model = new EventTableModel(sortedRevisions, [
						getColumnCount: {columnNames.size()},
						getColumnName: {index -> columnNames[index]},
						getColumnValue: {object, index ->
							object."${columnNames[index].toLowerCase()}"
						}] as TableFormat)

				return builder.table(id: 'revisions', model: model)
			}
		}

		//the 'show it' code
		builder.frame(title: 'SVN Change List', size: [500, 500], pack: true, show: true,
				defaultCloseOperation: WindowConstants.DISPOSE_ON_CLOSE) {
			scrollPane {
				def component = getTableModel(sout, serr)
				//I'm sure this could be more elegant
				if(component instanceof JTable){
					def tableSorter = new TableComparatorChooser(revisions,
						sortedRevisions, AbstractTableComparatorChooser.MULTIPLE_COLUMN_MOUSE)
				}
			}
		}
	}
}

UPDATE: A long time coming, but here’s a screenshot for you Sho.
svnglazedlistexamplescreen

Share
Get Adobe Flash player