At this point, the task can be compiled, installed, and executed.
When compiling this class, you want to set the include's AntRuntime to
true, so that the CLASSPATH includes the Ant classes that you're deriving from and using.

Once compiled, the .class file needs to be put in a .jar, and that
.jar copied into the lib directory of ANT_HOME. Now, to use our new task, one
final element needs to be added to a build.xml file.

<taskdef name="plistgen"
classname="com.isx.ant.plistgen.PListGen"/>

When the Javadoc target is run, the file should be created, but will be empty, since we haven't actually populated the packages yet.

The Path class has a number of methods, but two particularly convenient
methods pretty much do everything desired. First is the toString()
method, which will generate a tokenizer-separated list of the path
entries, where the token is correct for the platform (either a colon or a semicolon).
This can be (and is) used to set the -sourcepath parameter for the
Java and Javadoc command line scripts.

Second is the static method, translatePath, which takes the token-separated list and the value of the current project to create a
String array of absolute paths, using the project's basedir as the root.

For example, a call to translatePath with a project whose base is
c:\dev and the string src1:src2 will return a String array with
two strings, c:\dev\src1 and c:\dev\src2. The current project is a field of task that all tasks inherit.

String[] roots = Path.translatePath(project,
sourcePath.toString());

Now we have to iterate over the roots and their descendents to find
paths that represent packages. We define a package as a file that:

Is a child of a root (the roots will be in the top package, and as such, can't be added to Javadoc when packages are involved).

Is a directory.

Has Java source files inside.

Since the usual best way to iterate down a directory tree is recursion, we'll write a recursive helper method.

The final version has the addition of logging code both at the
INFO and VERBOSE levels, not included in this excerpt, but worth looking at.
The log() method defaults to INFO, which is displayed as output
automatically. Log calls with Project.MSG_VERBOSE will be shown when
Ant is called with the -verbose option, and task writers should keep
that in mind in helping their users debug their Ant buildfiles.

The JAXB Dependency Problem

Now that we're more familiar with making a task in Ant, we'll work on
a new one that solves the JAXB dependency problem, where the build
system shouldn't regenerate the .java files if the existing ones are complete and newer (more recently generated) than the last
modification of the two source files.

What we'd like to see as the task is the following, with three
parameters as attributes -- the DTD file, the xjs file, and the destination root.

This is much simpler than the Java task we used before, but at the same time gives us "type-safety" by eliminating the need to put the arguments
in the right order. After we get these basics working, we can
consider expanding the task to support the other parameters and options.

With these attributes, we could theoretically build an execute() method that calls the main() of the JAXB compiler (com.sun.tools.xjc.Main). I say "theoretically," because unfortunately, due to a security issue ("sealed jars") and the fact that the early access JAXB includes an XML parser that conflicts with the one in Ant (it was necessary to release an "unsealed" version of that jar to deal with this issue), we can't embed the JAXB runtime into Ant directly.
So we can't break out of using the Java task we got into in the last article.

We can still add the dependency checker, but instead of building it
into the task, we do things external to the JAXB task and only execute
the JAXB task if a particular property is set. It would be nice to
use uptodate, but that implementation (and its Mapper implementation) are all keyed to comparing a single file to a result file, and we need to compare two files simultaneously to a result file (since the two files are needed to determine what the result file is). So we have to write our own task to do something similar to uptodate.

We need to duplicate part of the JAXB algorithm by analyzing the DTD
and xjs files to determine what files will be created. Then we
need to compare the creation time of each of those files to the source
DTD and xjs files. If a file is missing, or if any file is older than
the DTD or xjs file, we set the property to regenerate the files.

So our execute() method, after checking that the attributes were all set (throwing BuildException if one is missing), adds:

If the property isn't set, then the JAXB target won't execute, and the
build process is much faster.

The algorithm for finding the file list from the source isn't included
here, but this can be done just using the DeclHandler and ContentHandler
of SAX2. However, setting up the DTD file and crimson isn't the easiest
thing to do -- if there's enough interest, I may write on that topic
in the future. But until then, you can look at the implementation
code for this article.