Code generation made easy using patterns

Since starting developing some code generation plugins (toString() generator and Java Util Logger Generator) using/for the NetBeans 6.1Beta I have learnt a lot and the more I worked on it I kept asking myself how could I make it easier. Once both the plugins took rudimentary form I learnt and discovered that patterns could make this task whole lot easier for me. So I started re-implementing the Logger generator (if you are interested in getting the resources please check the logger blog) plugin.

Now I wanted to implement that the plugin will search for System.out.print(ln) and out.print(ln) if static import exists. In doing so I had to walk the PARSED tree of a Java Source file. Just to give me a flavour that I am implementing something cool I called it JavaSourceTreeParser. Basically what it does is breaks down each every Java Statement to a form which can not be further decomposed. In the initial version the conversion was done in the parser it self (Revision 21). Then it felt that I could easily use Observer pattern for it and after implementing it (Revision 31) I saw that I am right and I achieved something which every code generator can use. (As I use GIT SCM I usually work offline and make bunch of commits together so dont worry how I make multiple commits at a go :)).

Once implementing it I found that for performance improvement and communication between various listeners I felt the need of session for state information sharing and then I decided to use Composite pattern for the purpose. and it also worked like a charm and as a result improving the overall performance of the plugins. The following diagram might give an idea how to use it using the API I designed.



What I implemented in the listener simply that the listener will get notified whenever the parser come across a particular type of tree, for System.out.println it would be MethodInvocationTree (Kind.METHOD_INVOCATION), and the listener according to its implementation will handle the Tree; in my case I replaced the method invocation with a logger invocation. In case where I inserted a Log method at the beginning of every method body I listened to Kind.METHOD. Now I am implementing Log insertion for Throw, Return and End of Method block. In process I will also add parsing life cycle listener to the API so that modification to the class is done only once when the parsing is completed. I hope this API helps users interested in code generation. I am very interested to learn what users think about it.

5 comments:

  1. Implementation for log injection before return and throw statements is now available (Revision 41).

    It requires minor change of removing old logs and adding the new ones; this feature will be added in a day or 2.

    We will also add logs to catch block.

    ReplyDelete
  2. is it possible you could have used the visitor pattern to walk the tree :)

    ReplyDelete
  3. Firstly, I wanted to abstract walking the tree part and thus I did not mention it :). Currently I am using Delegator pattern coupled with DFS.

    But yes, TreeVisitor also could be used, I did not take that approach, may be in version 2 :).

    ReplyDelete
  4. nice works,
    [i am pretending, this code generation is for generating dynamic code on my source files]
    since java supports source level annotation processor, i mean annotation which you can parse later. so how about if i wrote the following code -

    @GenerateLogger(...some properties...)
    class Bang { }

    so i might have better control over auto generation.
    anyway i think probable you have already implemented this stuff.

    not sure, but probable class tree or this class by default supports visitor pattern. may be i saw in annotation processor code.

    best wishes,

    ReplyDelete
  5. For controlling where to generate logs I will also add UI to it so that user can select and unselect options also user will have option to remove all logs.

    The reason for not using annotation was I want the user to be able to see the logs before compilation and add, remove, edit them as per his/her choice.

    Visitor pattern is good but not sufficient for my purpose. Because not only do I need to change a statement but I also need to make changes to the block/statement and for that I will need to the tree hierarchy that I am parsing. With Visitor it is possible to build the tree but in order to remove nodes from the tree it falls short(that is only have the tree path for/up-to the current node only). The aim of my work is to provide an API for code generation/modification where user has to do minimal work.

    ReplyDelete

Search