Search code examples
javajavadocannotation-processing

Fully-qualified class name of links while parsing Javadoc


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?


Solution

  • 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