Fikin Ant Tasks
Makeup for JavaDoc API
I've always thought that JavaDoc was a great think. And JavaDoc API an easy think. Reading Sun's tutorial and Javaworld article years ago taught me so.
I would still be thinking the same if it wasn't for one idea of mine I've wanted to do for quite some time. I wanted to generate Ant tasks description files out of the Java sources directly (or the so called now "antPropertiesFile" task). When I began this work I had it everything figured out upfront, everything planned and generally I felt confident what and how I had to do. But then, it should come as no surprise to you that what actually came out was something completely different: the "VelocityDoclet" and few Velocity templates. How did this happened is a subject to another story, perhaps not that different than any other, in this I'd like to get your attention to JavaDoc API solely.
You may ask yourself, what is it in JavaDoc API so interesting worth writing about? We will see into this together in just a moment. You may also wonder what the makeup has to do with this? Since makeup is a common technique to beautify and enhance ones facial features I though it makes perfect sense since what we will discuss now has quite much to do with beatifying JavaDoc API itself.
* Before you do any makeup you need right tools and cosmetics *
Lets start with "javadoc" itself? Did you ever noticed that "javadoc" is not "java"? This hardly could be of any reason to bother as long as you don't expect thinks as from "java". For example, if you write your own Doclet using Log4J and want to pass the system property "log4j.configuration" to it, how would you do it? You may think same was as with "java", by using "-D" command line switch. Well, not exactly. It turns out that the "javadoc" exhibit rather exotic command like support which does almost no resemblance to "java". In due course you may became convinced, just as I am, that JavaDoc was never thought as programmable application in first place and JavaDoc API in fact is an after math. And as such perhaps not that successful one.
Back on our little problem, if you dig inside JavaDoc sources you'd end up with a loophole, a solution to passing system properties to the command line of :
% javadoc -J-Dmy.prop=my.value ...
* Apply concealer to any red or discolored spots *
Doclets do exhibit a very peculiar interface to deal with ... command line parameters. I speak about "public static int optionLength(String option)" and "public static boolean validOptions(String options[][], DocErrorReporter reporter)". The peculiarities are two:
  • First your Doclet does not deal with all parameters at once as it is with good old "public static main(Stinrg[] args)" but rather one by one.
    JavaDoc main class will call "optionsLength" for each of the command line options it will encounter and ask your Doclet for how many part it is, imagine you're supposed to recognize the parameter "-test val1 val2", so "optnionsLength("test")" has to return 2.
  • Second, once command line parameters have been established (and everything is fine) JavaDoc main class will call "validateOptions("test")" to let you do their value validation.
    Method call with the validation also happens for parameters one by one.
I don't know what your opinion is but I find it rather strange way to parse command line, especially when this could have been done simply via "public static main(String[] args, RootDoc root)".
Dealing with command line parameters is not addressed in J2SDK neither in J2EE. Typically you'd have to resolve for your own solution or opt for 3rd party library. I personally use Apache's Jakarta Commons CLI which is utilized by AbstractDoclet. This Doclet if used as base class will simplify command line option handling to declarations only. Browse the link to see how this comes into play.
* Choose the right colors for foundation for your face *
The main Doclet method signature is "public static void start(RootDoc root)". This mimics main Java method "public static void main(String[] args)" so there is not much to e said about. Well, yes and no.
I'd like to bring the subject of "static" here. We all know that static methods are not suitable for serious object modeling, they simply does not allow for inheritance. I said in the beginning that JavaDoc API looks like an afterthought of JavaDoc. My main argument was that JavaDoc does have its own binary and main class which is not yours and mine Doclet class. JavaDoc API more or less is a "template" API. This makes Doclets not a main class-like but rather pluging-like. And this is implemented with static template methods!
Lets consider the case I ran into. I began with a single Doclet for printing ant.properties, then I discovered I'd like to have antlib.xml, then documentation in html format, then in text. This made for 4 very similar Doclets which happened to exhibit common handling of start(RootDoc). But how do you template start(RootDoc) method? It is possible with instance methods but static? And here the big problem happens, my maintenance effort began to escalate due to the lack of code reusage of 3 static methods (including the command line parsing methods).
Now, eventually I solved my problem by moving from static to instance methods template methods.
The solution I opted for was to prototype start(RootDoc), validateOptions() and optionsLength() as instance based template methods. This is done by AbstractDoclet where the subclasing Doclet has to implement:
  • startCustom(RootDoc) for main execution body
  • validateCustomOptions() for parsing the actual command line parameters
  • and place a prototype optionsLength(), which is requred simply because JVM does not properly initialize class hierarchy with static methods only
Browse the link to see how this comes into play.
* Highlight your brow bone and cheek bones *
One rather interesting aspect of JavaDoc API is the ClassDoc, MethodDoc and their class tree. After working on the 4 Doclets I was after, I became convinced that some important usability problems weren't addressed properly.
First, there are these interfaces for Tags. Obtaining Tags require looping which I never quite got to understand fully. And then there is the part with pooling for a Tag to test if existed and then to get its text was.
Second, there is this problem with class testing. You can't quite make test for "is A instanceof B" because ClassDoc is not java.lang.Class. This requires some quote looping over the ClassDoc hierarchy.
Third, there is is this inability to get ClassDoc with "all" the methods including the inherited one, something like java.lang.Class.
These are rather fundamental design questions to simply to pass by.
Solving all of this problems lead to creation of TemplateTools class which provide methods addressing these and some more problems.
By default it is made available to templates by VelocityDoclet.
* Line your eyes *
I'm not sure whether you've come already to the same idea as me that whatever we speak, what we really want from JavaDoc can be explained as the very simple function: javadoc(source) --> output document.
Or to be more precise javadoc(sources,template) --> output document.
There is some similarity with other technologies like Servlets and JSP. And if JSP is to be considered as the simplest then why not the same for JavaDoc as well?
I personally wondered little of what to settle for. Apache Jakarta Velocity is a template engine being around from several years and is suitable enough for generating all my Ant-related outputs.
Thus welcome to VelocityDoclet. This is a very straight forward Doclet which process Velocity Templates for given input Java sources.
There are two objects given by default to the templates:
  • root from start(RotDoc root) and
  • tools is an instance of TemplateTools
The rest is simply up to you to implement ;) Take a look at the exiting templates I've done for real life examples:
* You can always forget about makeup if you're not very happy with it *
This pretty much this wraps thinks on my side on the subject of JavaDoc API.
I hope you enjoyed the article as much as I did writing it. I also hope you found something useful beside my personal opinions ;-)
Have fun with the library and if you find something you'd like to contribute or comment, feel welcome to drop me an email.
©2006-2007 Nikolay Fiykov