Friday, November 12, 2010

Two nice wines and Baguette

I don't have a very good way to record memorable wines. So let me note a couple here from the last couple of months before I forget them: 2008 Langmeil Orphan Bank and 2004 Reschke Bos.

And while I'm typing epicuriously, let me also note that two work friends and I had a great meal at Baguette this week. All three of us noted the complementary flavours of our dishes - the duck, the rabbit, and my pork belly, boudin noir, lentils de puy, and garlic snails. Can't wait to go back there again.


Playing with Batik

Helen's group at school has been asked to put together an "interactive trade-show booth" on the topic of Indonesia. I decided that this was a good excuse to try to develop some kind of quiz to help. It seemed that a geographical one would be good.

The concept involved a map, and two types of questions: (a) the user is asked to click on a particular place (island, country, city), and (b) a particular place is highlighted visually on the map, and the user has to choose/guess the name of that place using a multiple choice answer.

I didn't want to build a full scale GIS, so SVG was the obvious answer. I found a great map of the world at wikimedia commons. Using Inkscape, I could crop it and choose an appropriate part of SE Asia - essentially a square covering Burma to Tasmania. The really nice thing about that map is that all the paths are neatly grouped into countries, and they're even labelled inside the SVG source. (The Inkscape XML Editor was invaluable for understanding the structure and locating different paths).

Although groovy is my new language of choice at work, I decided to use plain java/swing for this project, with Apache Batik for the SVG interface. It was easy to highlight a particular country by using the DOM interface to find elements with the appropriate id, and setting the style attribute. I found later that it was critical to do this in the correct thread, and also that you must wait at startup until the map has been rendered.

I found it somewhat frustrating though, to try to detect mouse clicks on the relevant countries. I found that there were two steps necessary: I had to register as a listener for the "click" event on the main layer element - that was obvious enough. But I also had to set an onclick attribute on that element. I don't really know why, but if I don't do it, it doesn't work.

  Element e = getElementById("layer1");
  EventTarget t = (EventTarget) e;
  t.addEventListener("click", this, false);
  e.setAttribute("onclick", "var x = 1;"); // not sure why

I was quite diligent with my test-driven development of the main controller, and used Mockito extensively. It really was fun, and (I'm convinced) much faster than trying to do a big-up-front-design.

The final stage was to package it all up as a single jar. I'd used Simon Tuffs' clever one-jar before at work, and knew that it would be the best thing to use. All of those 15 or so Batik jars, plus the ever-so-useful miglayout - I didn't want to have all those files floating around, having to manually set up a classpath.

There was one extremely frustrating part, though. I kept getting an "Invalid CSS Document" error, with this stacktrace:
Invalid CSS document.
mapquiz.jar (The system cannot find the file specified)
   at org.apache.batik.css.engine.CSSEngine.parseStyleSheet(CSSEngine.java:1149)
   at org.apache.batik.dom.svg.SVGDOMImplementation.createCSSEngine(SVGDOMImplementation.java:117)
   at org.apache.batik.dom.ExtensibleDOMImplementation.createCSSEngine(ExtensibleDOMImplementation.java:212)
   at org.apache.batik.bridge.BridgeContext.initializeDocument(BridgeContext.java:378)
   at org.apache.batik.bridge.GVTBuilder.build(GVTBuilder.java:55)
   at org.apache.batik.swing.svg.GVTTreeBuilder.run(GVTTreeBuilder.java:96)


I didn't even think that I needed or was using any CSS. After googling unproductively, I looked at the source, and discovered line 113 in SVGDOMImplementation.createCSSEngine()
 URL url = getClass().getResource("resources/UserAgentStyleSheet.css");

I tried simply making a resources directory and CSS file inside my one-jar, but that didn't work. After sorting out the differences between Class.getResource() and ClassLoader.getResource(), I decided that it was going to be looking for /org/apache/batik/dom/svg/resources/UserAgentStyleSheet.css. So I created that nest of directories, jar'd up the one-jar, and it worked. There's no way that I'm a classloader expert, but that area of java is now a little less mysterious than it once was.

Just need to see what grade I get now...