我已经为我的新工具链进行了几周的原型设计,它几乎可以为更广泛的受众准备了。它基于Javadoc和XHTML,我很快就会写一篇博客介绍它。
今天,我想展示的只是一个帮助我进行单元测试和运行原型的单个类。如果你曾经编写过Javadoc doclet,你可能会理解为什么这很有用
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.ClassDoc;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javadoc.JavadocTool;
import com.sun.tools.javadoc.ModifierFilter;
import com.sun.tools.javadoc.PublicMessager;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
public class EasyDoclet {
final private Logger log = Logger.getLogger(EasyDoclet.class.getName());
final private File sourceDirectory;
final private String[] packageNames;
final private File[] fileNames;
final private RootDoc rootDoc;
public EasyDoclet(File sourceDirectory, String... packageNames) {
this(sourceDirectory, packageNames, new File[0]);
}
public EasyDoclet(File sourceDirectory, File... fileNames) {
this(sourceDirectory, new String[0], fileNames);
}
protected EasyDoclet(File sourceDirectory, String[] packageNames, File[] fileNames) {
this.sourceDirectory = sourceDirectory;
this.packageNames = packageNames;
this.fileNames = fileNames;
Context context = new Context();
Options compOpts = Options.instance(context);
if (getSourceDirectory().exists()) {
log.fine("Using source path: " + getSourceDirectory().getAbsolutePath());
compOpts.put("-sourcepath", getSourceDirectory().getAbsolutePath());
} else {
log.info("Ignoring non-existant source path, check your source directory argument");
}
ListBuffer<String> javaNames = new ListBuffer<String>();
for (File fileName : fileNames) {
log.fine("Adding file to documentation path: " + fileName.getAbsolutePath());
javaNames.append(fileName.getPath());
}
ListBuffer<String> subPackages = new ListBuffer<String>();
for (String packageName : packageNames) {
log.fine("Adding sub-packages to documentation path: " + packageName);
subPackages.append(packageName);
}
new PublicMessager(
context,
getApplicationName(),
new PrintWriter(new LogWriter(Level.SEVERE), true),
new PrintWriter(new LogWriter(Level.WARNING), true),
new PrintWriter(new LogWriter(Level.FINE), true)
);
JavadocTool javadocTool = JavadocTool.make0(context);
try {
rootDoc = javadocTool.getRootDocImpl(
"",
null,
new ModifierFilter(ModifierFilter.ALL_ACCESS),
javaNames.toList(),
new ListBuffer<String[]>().toList(),
false,
subPackages.toList(),
new ListBuffer<String>().toList(),
false,
false,
false);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
if (log.isLoggable(Level.FINEST)) {
for (ClassDoc classDoc : getRootDoc().classes()) {
log.finest("Parsed Javadoc class source: " + classDoc.position() + " with inline tags: " + classDoc.inlineTags().length );
}
}
}
public File getSourceDirectory() {
return sourceDirectory;
}
public String[] getPackageNames() {
return packageNames;
}
public File[] getFileNames() {
return fileNames;
}
public RootDoc getRootDoc() {
return rootDoc;
}
protected class LogWriter extends Writer {
Level level;
public LogWriter(Level level) {
this.level = level;
}
public void write(char[] chars, int offset, int length) throws IOException {
String s = new String(Arrays.copyOf(chars, length));
if (!s.equals("\n"))
log.log(level, s);
}
public void flush() throws IOException {}
public void close() throws IOException {}
}
protected String getApplicationName() {
return getClass().getSimpleName() + " Application";
}
}
如果你想测试你的doclet或以编程方式运行它,你必须使用javadoc命令行工具或随tools.jar提供的邪恶主类- 它是邪恶的,因为它在完成时会调用System.exit()
,在单元测试中不可用。所以我在这里深入研究了JDK源代码,以了解如何在不带所有附件的情况下以编程方式启动一个 Doclet
。(再次提醒,你很可能在尝试编写自己的Doclet之前不会理解这一点。该API非常旧,非常糟糕。)
package com.sun.tools.javadoc;
import com.sun.tools.javac.util.Context;
import java.io.PrintWriter;
/**
* Protected constructors prevent the world from exploding!
*/
public class PublicMessager extends Messager {
public PublicMessager(Context context, String s) {
super(context, s);
}
public PublicMessager(Context context, String s, PrintWriter printWriter, PrintWriter printWriter1, PrintWriter printWriter2) {
super(context, s, printWriter, printWriter1, printWriter2);
}
}
哦,你还需要这个
EasyDoclet doclet = new EasyDoclet(new File("/my/source"), "some.package", "another.package");
RootDoc doc = doclet.getRootDoc();
...
这就是你在单元测试(或任何代码)中使用它的方法