原文地址:http://hi.baidu.com/topguntopgun/blog/item/237cd5569a404948564e0096.html
基础
Doclet 是用 JavaTM编程语言编写的程序,它用 doclet API 指定 Javadoc 工具的输出内容和格式。缺省情况下,Javadoc 工具用 SunTM 提供的“标准”doclet 来生成 HTML 形式的 API 文档。然而,用户也可用自己的 doclet 根据个人喜好自定义 Javadoc 输出。用户可以利用 doclet API从头开始编写 doclet,也可以对标准 doclet 进行修改,以适合自己的需要。
下面是创建和使用自己的 doclet 的基本步骤:
- 编写用来构成 doclet 的 Java 程序。为使用 doclet API,该程序应导入 com.sun.javadoc.*。程序的入口点是一个带有 public static boolean start 方法的类,它将 RootDoc 作为参数。
- 编译 doclet。可以使用 Java Development Kit 的编译器 javac 来编译它。
- 用 -doclet <YourDoclet> 选项运行 javadoc 工具,生成 doclet 指定的输出。
doclet API 类文件在 JDK 软件的 lib/tools.jar 文件中。当编译 doclet 和使用自定义 doclet 时,tools.jar 必须在类路径上。为此,可以对 javac 和 javadoc 使用 -classpath 选项。
如果运行 javadoc 时未使用 -doclet 命令行选项,则按缺省状态的标准 doclet 生成 HTML 格式的 API 文档。
包 com.sun.javadoc 由定义 doclet API 的接口组成。JDKTM 软件的 lib/tools.jar 文件包含这些接口及实现这些接口的类的私有包。tools.jar 文件还包括实现标准 doclet 的类。
简单示例
参看下面由一个短类组成的简单示例 doclets,您可以对 doclets 的运行方式有所了解:
import com.sun.javadoc.*;
public class ListClass {
public static boolean start(RootDoc root) {
ClassDoc[] classes = root.classes();
for (int i = 0; i < classes.length; ++i) {
System.out.println(classes[i]);
}
return true;
}
}
通过查看代码,不难猜到:该 doclet 将取出 Javadoc 所操作的一些类,然后将它们的名称输到标准输出上。
就该 doclet 而言,首先应注意到:为使用 doclet API,程序导入了 com.sun.javadoc.*。对于所有 doclets,入口点均为 public static boolean start 方法。start 方法将 RootDoc 作为参数使用。该参数携带运行 javadoc 时命令行指定的所有选项信息,以及 Javadoc 所操作的类和包的信息。
RootDoc 定义返回 ClassDoc 数组的 classes 方法,其中数组的元素代表 Javadoc 解析的类。随后,for 循环输出数组中每个类的名称(将 ClassDoc 传给 println 将输出 ClassDoc 代表的类名)。
运行该 doclet 前,必须先进行编译。可以用 JDK javac 编译器来编译它。API 类文件在 JDK 软件的文件 lib/tools.jar 中,因此编译器的 classpath 中需包括 tools.jar,对本例如下所示:
javac -classpath C:\jdk1.2\lib\tools.jar ListClass.java
为运行 ListClass doclet,您必须用 Javadoc 的 -doclet 标记指向编译后的 doclet。例如,要在文件 MyClass.java 上运行 doclet,需使用命令:
% javadoc -doclet ListClass -classpath C:\jdk1.2\lib\tools.jar MyClass.java
输出结果将是字符串 "MyClass"。注意:该命令也要求类路径中包含 tools.jar。
关于命令行选项的说明:如果运行 javadoc -help,就会看到 Javadoc 工具有两个命令行选项集。一个选项集是普通选项集,它适用于任何 doclet。另一个则专门用于标准 doclet。使用自定义 doclets 时,将不能使用第二个选项集。自定义 doclets 可以定义自己的命令行选项。参阅下面的示例。
要生成 API 文档,doclet 将会比该示例要复杂得多。如果想自定义 Javadoc 生成的 API 文档格式,可能应该从缺省标准 doclet 起步,必要时对其进行修改,而不是完全从头开始编写 doclet。
自定义 Javadoc 输出
为了自定义 Javadoc 工具的输出,需要编写自己的 doclet,用于指定所需的输出格式和内容。如果希望 HTML 输出与缺省的输出大体相同,则可以用标准 doclet 作为创建 doclet 的起点。您可以在标准 doclet 中扩展相应的类,必要时添加或覆盖方法,以生成符合要求的输出。也可以复制整个标准 doclet 并进行修改。假如以标准 doclet 的副本为起点,就要删除每个源文件开始位置的 package 语句,并将这些语句改为自己的新包的名称。
<!-- As a trivial example, suppose you want to customize the HTML output generated by Javadoc so that the horizontal rules were bolder and thicker than the default horizonal rules in the standard output. How would you go about modifying the APIs in the standard doclet to produce these to changes? Because the customization in question involves a change in the HTML formatting, the above <a href="#api">API summary</a> suggests that you might need to modify the APIs in class <tt>HtmlWriter</tt>. If you look at the <a href="source/HtmlWriter.java">source</a> for the HtmlWriter class, you will find methods for writing the HTML tags used in generating the default HTML documentation. Among these, there is a method for writing the HTML tag for horizonal rules: <blockquote> <pre> public void hr() { println("<hr>"); } </pre> </blockquote> You'll also notice that the HtmlWriter class doesn't provide a way to produce the custom horizontal rules that you want. You can therefore add a method to HtmlWriter, or an HtmlWriter subclass, that prints the custom tag that you want. For example, you might add this method: <blockquote> <pre> public void hrCustom() { println("<hr size=3 noshade>"); } </pre> </blockquote> and call it instead of the <tt>hr</tt> method when you want to your output to use the thicker horizontal rules. <p> -->
假定在文档注释中除标准标记(例如 @param 和 @return)之外,您还想使用自定义标记 @mytag。为了利用自定义标记的信息,需要在自己的 doclet 中使用代表自定义标记的 Tag 的实例。为此,最简单的办法是使用 Doc 或 Doc 子类的 tags(String) 方法。该方法返回 Tag 数组,代表其名称与字符串参数匹配的所有标记。例如,如果 method 是 MethodDoc 的实例,那么
method.tags("mytag")
将返回 Tag 对象的数组,它代表该方法文档注释中的所有 @mytag 标记。然后即可用 Tag 的 text 方法访问 @mytag 标记的信息。此方法将返回一个字符串,代表必要时可以解析或使用的标记的内容。例如,如果文档注释包含的自定义标签为:
@mytag Some dummy text.
则 text 方法将返回字符串 "Some dummy text."。
下面的 doclet 即使用上述思想输出它查找到的特定标记的所有实例中所包含的内容:
import com.sun.javadoc.*;
public class ListTags {
public static boolean start(RootDoc root){
String tagName = "mytag";
writeContents(root.classes(), tagName);
return true;
}
private static void writeContents(ClassDoc[] classes, String tagName) {
for (int i=0; i < classes.length; i++) {
boolean classNamePrinted = false;
MethodDoc[] methods = classes[i].methods();
for (int j=0; j < methods.length; j++) {
Tag[] tags = methods[j].tags(tagName);
if (tags.length > 0) {
if (!classNamePrinted) {
System.out.println("\n" + classes[i].name() + "\n");
classNamePrinted = true;
}
System.out.println(methods[j].name());
for (int k=0; k < tags.length; k++) {
System.out.println(" " + tags[k].name() + ": "
+ tags[k].text());
}
}
}
}
}
}
doclet 所查找的标记由变量 tagName 指定。字符串 tagName 的值可以是自定义或标准的任意标记名。
该 doclet 将写到标准输出,但它的输出格式是可以修改的。例如,可以将 HTML 输出写到文件中。
用户可以编写接受自定义命令行选项的 doclets。为了解其工作原理,不妨让我们扩充一下上面的 doclet
示例,使它可以允许用户用命令行选项指定要查找的标记名。
任何使用自定义选项的 doclet 必须有一个名为 optionLength(String option) 的方法,该方法返回 int。对于想要 doclet 识别的每个自定义选项,optionLength 必须返回选项的组成部分数或符号数。示例中,我们想使用 -tag mytag 形式的自定义选项。该选项有两个部分:-tag 选项自身及其值。因此 doclet 中的 optionLength 方法必须为 -tag 选项返回 2。对于无法识别的选项,optionsLength 方法应返回 0。
下面是整个扩充后的 doclet:
import com.sun.javadoc.*;
public class ListTags {
public static boolean start(RootDoc root){
String tagName = readOptions(root.options());
writeContents(root.classes(), tagName);
return true;
}
private static void writeContents(ClassDoc[] classes, String tagName) {
for (int i=0; i < classes.length; i++) {
boolean classNamePrinted = false;
MethodDoc[] methods = classes[i].methods();
for (int j=0; j < methods.length; j++) {
Tag[] tags = methods[j].tags(tagName);
if (tags.length > 0) {
if (!classNamePrinted) {
System.out.println("\n" + classes[i].name() + "\n");
classNamePrinted = true;
}
System.out.println(methods[j].name());
for (int k=0; k < tags.length; k++) {
System.out.println(" " + tags[k].name() + ": " + tags[k].text());
}
}
}
}
}
private static String readOptions(String[][] options) {
String tagName = null;
for (int i = 0; i < options.length; i++) {
String[] opt = options[i];
if (opt[0].equals("-tag")) {
tagName = opt[1];
}
}
return tagName;
}
public static int optionLength(String option) {
if(option.equals("-tag")) {
return 2;
}
return 0;
}
public static boolean validOptions(String options[][],
DocErrorReporter reporter) {
boolean foundTagOption = false;
for (int i = 0; i < options.length; i++) {
String[] opt = options[i];
if (opt[0].equals("-tag")) {
if (foundTagOption) {
reporter.printError("Only one -tag option allowed.");
return false;
} else {
foundTagOption = true;
}
}
}
if (!foundTagOption) {
reporter.printError("Usage: javadoc -tag mytag -doclet ListTags ...");
}
return foundTagOption;
}
}
在修改后的 doclet 中,变量 tagName 是用命令行选项 -tag 设置的。对于自定义选项,它的 optionLength
方法返回 2。注意:这里并不需要 optionLength 的显式调用。
该 doclet 还增加了 readOptions 方法,用于解析命令行选项以查找 -tag 选项。它所利用的是这样一个事实:Rootdoc.options 方法返回包含选项信息的二维 String 数组。例如,假定命令为:
javadoc -foo this that -bar other ...
RootDoc.options 方法将返回
options()[0][0] = "-foo"
options()[0][1] = "this"
options()[0][2] = "that"
options()[1][0] = "-bar"
options()[1][1] = "other"
数组第二个下标中的元素个数由 optionLength 方法决定。本例中,对于选项 -foo,optionLength 返回 3;
对于选项 -bar,则返回 2。
validOptions 方法为可选方法,可以用来测试命令行标记用法的有效性。如果 validOptions 方法存在,就会被自动调用,而无需显式调用。如果选项用法有效,则该方法应返回 true,否则返回 false。当发现命令行选项的使用不正确时,还可从 validOptions 输出相应的错误信息。该示例 doclet 的 validOptions 方法检查是否使用了 -tag 选项以及是否仅使用了一次。
RootDoc参考:http://velociraptor.mni.fh-giessen.de/Java/jdk142/docs/tooldocs/javadoc/doclet/com/sun/javadoc/RootDoc.html