Ok, so I'm up late and fancy playing around with Groovy builders. How about a builder for graphviz dot files I think to myself? So I took the dot example (image) and made it builder friendly. To do so I changed:
digraph unix {
size="6,6";
node [color=lightblue2, style=filled];
"5th Edition" -> "6th Edition";
...To:new DotBuilder().dot {
size "6,6";
node( [color:'lightblue2', style:'filled'] );
from "5th Edition"; to "6th Edition";
...This code then exercises the required builder and displays a dot graph image in a swing frame (using the swing builder):import groovy.swing.SwingBuilderThe code for the builder is as follows:
import java.awt.BorderLayout
class DotBuilderExample {
static void main(args) {
def image = new DotBuilder().dot {
size "6,6";
node( [color:'lightblue2', style:'filled'] );
from "Got Java"; to "Got Groovy"; to "Got DotBuilder";
from "Got Graphviz"; to "Got DotBuilder";
}.image
def frame = new SwingBuilder().frame(title:'Example Dot', size:[300,300]) {
borderLayout()
label(icon:imageIcon(image))
}
frame.pack()
frame.show()
}
}
import groovy.util.BuilderSupportNote: There are in fact closing triple quotes on the long line above.
import javax.imageio.ImageIO
class DotBuilder extends BuilderSupport {
private def out
private def outTarget
private def from, to
DotBuilder() {
outTarget = new StringWriter()
out = new PrintWriter(outTarget)
}
protected void setParent(Object parent, Object child) { }
protected Object createNode(Object name) {
if (name == 'dot') return this
}
protected Object createNode(Object name, Object value) {
if (name == 'from') {
from = value
} else if (name == 'to') {
out.println """"$from" -> "$value";"""
from = value // lets us chain them
} else {
out.println """$name="$value";"""
}
}
protected Object createNode(Object name, Map attributes) {
out.println """$name [${attributes.collect { "$it.key=$it.value" }.join(", ")}];"""
}
protected Object createNode(Object name, Map attributes, Object value) { }
protected void nodeCompleted(Object parent, Object node) { }
def getImage() {
Process p = Runtime.getRuntime().exec("dot -Tgif")
p.outputStream.withStream { stream ->
stream << "digraph unix { $outTarget }"
}
p.waitFor()
ImageIO.read(p.inputStream)
}
}
6 comments:
That looks great! have you considered using FactoryBuilderSupport instead? that way you may support more nodes in a similar fashion as SwingBuilder does. I'd love to have some level of integration between DOTBuilder and GraphicsBuilder ;-)
And who knows maybe even integrate it with the upcoming Griffon's CompositeBuilder =)
Cheers,
Andres
It's nice to blog about a topic and have you find me. I didn't know about GraphicsBuilder. It looks powerful. Groovy extensions though? JAI for instance is great, but I wouldn't typically use it just because it needs the extra step of tweaking the deployment environment in order to get it to work.
I'm looking at FactoryBuilderSupport right now. Thanks for that.
The graphviz structure isn't really the best example for a groovy builder because it's much more list data than tree data. That and my code was really a dirty hack to get the syntax accepted.
Could you point me at this CompositeBuilder? A quick search only gets me obscure commit comments in FishEye like "changes to support CompositeBuilder/UberBuilder".
Ah. Ok. Griffon isn't a person's surname. More interesting material. Found the UberBuilder. Thanks.
I've been wanting to pick and choose parts of Grails, and if Griffon's doing this it could be the start a of rewarding trend.
Yes, Griffon is the first piece of the Swing/Grails puzzle. CompositeBuilder will be able to mix&match any builder based on FBS.
GraphicsBuilder relies on Java2D only, no need to meddle with JAI
Thansk about your DotBuilber!
One thing I could add to your
code, when running under windows:
def getImage() {
Process p = Runtime.getRuntime().exec("dot -Tgif")
p.outputStream.withStream { stream ->
stream << "digraph unix { $outTarget }"
}
def os = System.getenv("OS")
if (os == null && os.indexOf("Windows") != -1)
p.waitFor()
ImageIO.read(p.inputStream)
}
Tuomas
Hi Tuomas,
This code looks odd as the effect is to never call waitFor:
if (os == null && os.indexOf("Windows") != -1) p.waitFor()
Perhaps calling waitFor is redundant? I'll get around to trying the code on a Windows system to see what the problem with it is.
Thanks,
Merlyn
Post a Comment