In a Java annotation processor, I use DocTrees#getDocCommentTree(Element) to obtain a DocCommentTree, which I walk over with a visitor. Visitor's visitLink(LinkTree,C) method is invoked for found {@link tokens. For a {@link Foo}, LinkTree#getReference().getSignature() returns Foo, though it doesn't give you the fully-qualified class name. That is, is it java.lang.Foo? Is it Foo in the same package? Is it some Foo class imported? How can I get the fully qualified name of the reference while parsing links in Javadoc?
1 Answer
While parsing Javadoc in an annotation processor, we can first create an ImportTree and use it to determine the FQCN of a ReferenceTree returned by LinkTree#getReference(), that is, the FQCN in {@link signature label}. I have already implemented this for log4j-docgen in this commit. The pseudo-code is as follows:
public class ExampleProcessor extends AbstractProcessor {
private DocTrees docTrees;
private Trees trees;
@Override
public synchronized void init(final ProcessingEnvironment processingEnv) {
super.init(processingEnv);
docTrees = DocTrees.instance(processingEnv);
trees = Trees.instance(processingEnv);
}
private void scanDocTree(Element element) {
Map<String, String> imports = collectElementImports(element);
ExampleContext context = new ExampleContext(imports);
DocCommentTree tree = docTrees.getDocCommentTree(element);
ExampleDocTreeVisitor visitor = new ExampleDocTreeVisitor();
tree.accept(visitor, context);
}
private static final class ExampleDocTreeVisitor<Void, ExampleContext>
extends SimpleDocTreeVisitor<Void, ExampleContext> {
@Override
public Void visitLink(LinkTree linkTree, final ExampleContext context) {
String signature = linkTree.getReference().getSignature();
String fqcn = context.imports.get(signature);
// ...
return super.visitLink(node, data);
}
}
private Map<String, String> collectElementImports(Element element) {
ImportCollectingTreeScanner scanner = new ImportCollectingTreeScanner();
TreePath treePath = trees.getPath(element);
scanner.scan(treePath.getCompilationUnit(), null);
return scanner.imports;
}
private static final class ImportCollectingScanner
extends TreeScanner<Object, Trees> {
private final Map<String, String> imports = new HashMap<>();
@Override
public Object visitImport(ImportTree importTree, Trees trees) {
Tree qualifiedIdentifier = importTree.getQualifiedIdentifier();
String qualifiedClassName = qualifiedIdentifier.toString();
String simpleClassName = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1);
imports.put(simpleClassName, qualifiedClassName);
return super.visitImport(importTree, trees);
}
}
}
Credits
- @piotr-p-karwasz for the
ImportTreehint - Andi's blog on how to extract
ImportTreein an annotation processor
1 Comment
Piotr P. Karwasz
DocTrees extends Trees, so you can use a single field in your example.
com.foo.Bar, you can link to it using{@link Bar}.importstatements which don’t end with an asterisk take precedence over everything else. If noimportstatement specifies the class name, the doclet will look in the same package as the referencing class.