Fully-qualified class name of links while parsing Javadoc

42 views Asked by At

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

There are 1 answers

1
Volkan Yazıcı On BEST 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 ImportTree hint
  • Andi's blog on how to extract ImportTree in an annotation processor