|
|
||
Rémi Forax's BlogClosure and groovy builderPosted by forax on April 01, 2008 at 03:47 AM | Comments (5)
One think i really like in Groovy, it's its concept of
Builder.
An HTML tree in Groovy is defined like that
html {
head {
title "hello groovy builder"
}
}
JavaFX uses a quite similar syntax but the syntax is built-in. The beauty of the groovy beast is that the builder syntax relies on closure. Closure The trick is the following:
Ok, so let's try to do the same in Java using the BGGA closure proposal. CICE is too verbose for that case and it should work seamlessly with FCM+ JCA Proxiiiiiiiiii
But before, how to define all the allowed markups of
an XML dialect ?
public interface XHTMLBuilder {
public void html(Object textOrClosure);
public void head(Object textOrClosure);
public void title(String text);
public void body(Object textOrClosure);
public void h1(Object textOrClosure);
public void br();
}
A Proxy acts as a multiplexer a call to any of its methods is redirected to a single generic method named invoke.
public class XMLBuilderFactory {
final Appendable appendable;
public XMLBuilderFactory(Appendable appendable) {
this.appendable=appendable;
}
public void text(CharSequence seq) {
try {
appendable.append(seq);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public <T> T createBuilder(Class<T> generatorInterface) {
return generatorInterface.cast(Proxy.newProxyInstance(
generatorInterface.getClassLoader(),
new Class<?>[]{generatorInterface},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
...
return null;
}
}));
}
}
Here appendable
acts as a writable stream of characters,
something on which I can append characters.
Java Builder
Now, suppose that the method invoke is written,
we will see after how to write it.
public class Main {
public static void main(String[] args) {
XMLBuilderFactory factory=new XMLBuilderFactory(System.out);
XHTMLBuilder b=factory.createBuilder(XHTMLBuilder.class);
b.html({=>
b.head({=>
b.title("hello Java 7 builder");
});
b.body({=>
b.h1("Hello");
factory.text("Greetings from");
b.br();
factory.text("Java 7 builder");
});
});
}
}
It's even more readable if we use the control invocation syntax.
b.html() {
b.head() {
b.title("hello Java 7 builder");
}
b.body() {
b.h1("Hello");
factory.text("Greetings from");
b.br();
factory.text("Java 7 builder");
}
}
This construct has several advantages. I can't produce ill formed XML, markup are opened and closed by the same code. Because the builder uses an interface I can't create markup with the wrong name. Even better, refactoring the markup name will works ! How to write the method invoke ?
For each argument of the method, i have
to determine if it's a closure or any other objects.
If it's a closure, i have to invoke it.
If it's another object, i can append it into the
Appendable.
if (arg instanceof {=>void}) {
(({=>void})arg).invoke();
}
There is two poblem with that code.
First, there are two kinds of closure (function type),
unrestricted and restricted one.
A restricted closure is a closure that can't break the control
flow using break, continue or return.
I am not sure i want my closure be able to break the
control flow but the control invocation syntax creates
unrestricted closure, so i need unrestricted closure.
So instead of using '=>' in the function type, i have to use
'==>'
if (arg instanceof {==>void}) {
(({==>void})arg).invoke();
}
But there is another problem, currently,
{==>void} is a parameterized type and so
it's illegal to use it in an instanceof.
Here, i have ask Neal, he says
he planed to make "types such as this one non-generic".
String name=method.getName();
if (args==null || args.length==0) {
appendable.append('<'+name+"/>");
return null;
}
appendable.append('<'+name+'>');
try {
for(Object arg:args) {
if (arg instanceof javax.lang.function.unrestricted.V) {
((javax.lang.function.unrestricted.V)arg).invoke();
} else {
appendable.append(arg.toString());
}
}
} finally {
appendable.append(""+name+'>');
}
A zip containing the wole codes is here:
closure-builder.zip
Cheers,
Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment
| ||
|
|