Skip to main content

Closure and groovy builder

Posted by forax on April 1, 2008 at 3:47 AM PDT

One think i really like in Groovy, it's its concept of
Builder.


It allows to simply create tree of objects like XML trees
using a concise syntax.

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:

  1. An XML node is a method.
  2. The content of a XML Node is a closure
    taken as argument of the method

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 ?


Ok, we need one method by markup, but because the code is identical
for two different markups, instead of writing boilerplate code,
I will use the class
java.lang.reflect.Proxy.


To use the Proxy, we have to define an interface. The one below
allow us to generate XHTML.

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.


The method createBuilder takes an interface as
argument and returns a class that implements that interface.
All methods of thi interface are routed to the method
invoke of the InvocationHandler.

Java Builder

Now, suppose that the method invoke is written,
we will see after how to write it.


To write an XHTML tree on the standard ouput,
using the BGGA closure syntax, the code is:

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.


The question is how to dynamically test if an object
is or not a closure.


I don't want any closure i just want a closure that takes
no parameter and returns void, a {=>void}.

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


By the way, i think the spec should be revised
to swap the two notations ('=>' and '==>'),
'=>' should be used for unrestricted closure.
If i want a restricted closure which is not the default case,
i have to take care about that and use the '==>' syntax.
Append a second '=' is equivalent to say
don't worry compiler i know what i'am doing.


So currently, the code should be:

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


There is a workaround, currently
the closure prototype works with 1.4/1.5 VM
and thus generates interfaces corresponding to
function types.
The interface corresponding to the raw type of {==>void} is
javax.lang.function.unrestricted.V.


Here is the code that works:

  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("');
  }

A zip containing the wole codes is here:
closure-builder.zip



You need the
prototype of the BGGA
closure too.

Cheers,


Rémi

Related Topics >>

Comments

Rather than doing instanceof tests, you should overload the methods.

Hey guy what a groovy idea you have here! Thank you akiri for sharing it with us

I agree with akiri... what about a closure and groovy notation Builder for students ?

hmm interesting and what about our school notation ?

It might be a good a Idea to create a notation software because the random notation software used for system has a lof a bugs ... By the way it has been 125 days between the moment we ended the concurency exam and today and we still have nothing !!!

Nice experiment, but I still have a preference for the Groovy builder ;-)