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.
Implementation for log injection before return and throw statements is now available (Revision 41).
ReplyDeleteIt 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.
is it possible you could have used the visitor pattern to walk the tree :)
ReplyDeleteFirstly, I wanted to abstract walking the tree part and thus I did not mention it :). Currently I am using Delegator pattern coupled with DFS.
ReplyDeleteBut yes, TreeVisitor also could be used, I did not take that approach, may be in version 2 :).
nice works,
ReplyDelete[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,
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.
ReplyDeleteThe 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.