All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jvnet.hudson.annotation_indexer.AnnotationProcessorImpl Maven / Gradle / Ivy

The newest version!
package org.jvnet.hudson.annotation_indexer;

import org.kohsuke.MetaInfServices;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import static javax.lang.model.SourceVersion.RELEASE_6;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * Creates indices of {@link Indexed} annotations.
 *
 * @author Kohsuke Kawaguchi
 */
@SupportedSourceVersion(RELEASE_6)
@SupportedAnnotationTypes("*")
@SuppressWarnings({"Since15"})
@MetaInfServices(Processor.class)
public class AnnotationProcessorImpl extends AbstractProcessor {
    /**
     * Use of an annotation.
     */
    private final class Use {
        /**
         * FQCN of the annotation.
         */
        final String annotationName;
        /**
         * Strings that designate FQCNs where annotations are used, either on a class or its members.
         */
        final Set classes = new TreeSet();
        /**
         * Keeps track of elements that has the annotation.
         */
        final Set originatingElements = new HashSet();

        private Use(String annotationName) {
            this.annotationName = annotationName;
            try {
                classes.addAll(loadExisting());
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
            }
        }

        void add(Element elt) {
            originatingElements.add(elt);

            TypeElement t;
            switch (elt.getKind()) {
            case CLASS:
            case INTERFACE:
            case ANNOTATION_TYPE:
            case ENUM:
                t = (TypeElement) elt;
                break;
            case METHOD:
            case FIELD:
                t = (TypeElement) elt.getEnclosingElement();
                break;
            default:
                throw new AssertionError(elt.getKind());
            }
            classes.add(getElementUtils().getBinaryName(t).toString());
        }

        String getIndexFileName() {
            return "META-INF/annotations/" + annotationName;
        }

        /**
         * Loads existing index, if it exists.
         */
        List loadExisting() throws IOException {
            List elements = new ArrayList();
            try {
                FileObject in = processingEnv.getFiler().getResource(CLASS_OUTPUT, "", getIndexFileName());
                // Read existing annotations, for incremental compilation.
                BufferedReader is = new BufferedReader(new InputStreamReader(in.openInputStream(),"UTF-8"));
                try {
                    String line;
                    while ((line=is.readLine())!=null)
                        elements.add(line);
                } finally {
                    is.close();
                }
            } catch (FileNotFoundException x) {
                // OK, created for the first time
            }
            return elements;
        }

        void write() {
            try {
                FileObject out = processingEnv.getFiler().createResource(CLASS_OUTPUT,
                        "", getIndexFileName(),
                        originatingElements.toArray(new Element[originatingElements.size()]));

                PrintWriter w = new PrintWriter(new OutputStreamWriter(out.openOutputStream(),"UTF-8"));
                try {
                    for (String el : classes)
                        w.println(el);
                } finally {
                    w.close();
                }
            } catch (IOException x) {
                processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
            }
        }
    }

    private Elements getElementUtils() {
        return processingEnv.getElementUtils();
    }

    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        if (roundEnv.processingOver())
            return false;
        
        // map from indexable annotation names, to actual uses
        Map output = new HashMap();
        scan(annotations, roundEnv, output);
        for (Use u : output.values())
            u.write();
        return false;
    }

    private AnnotationMirror findAnnotationOn(Element e, String name) {
        for (AnnotationMirror a : getElementUtils().getAllAnnotationMirrors(e))
            if (getElementUtils().getBinaryName((TypeElement) a.getAnnotationType().asElement()).contentEquals(name))
                return a;
        return null;
    }

    private void scan(Set annotations,
            RoundEnvironment roundEnv, Map output) {
        for (TypeElement ann : annotations) {
            AnnotationMirror indexed = findAnnotationOn(ann,Indexed.class.getName());
            if (indexed == null)
                continue;   // not indexed

            AnnotationMirror retention = findAnnotationOn(ann, Retention.class.getName());
            if (retention == null) {
                processingEnv.getMessager().printMessage(Kind.WARNING, "Specify @Retention(RUNTIME)", ann);
            } else {
                // XXX check that it is RUNTIME?
            }

            String annName = getElementUtils().getBinaryName(ann).toString();
            Use o = output.get(annName);
            if (o==null)
                output.put(annName,o=new Use(annName));

            for (Element elt : roundEnv.getElementsAnnotatedWith(ann)) {
                AnnotationMirror marked = findAnnotationOn(elt,annName);
                assert marked != null;

                // TODO: validator support

                o.add(elt);
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy