package org.checkerframework.javacutil; import java.util.HashSet; import java.util.Set; import javax.annotation.processing.*; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.comp.CompileStates.CompileState; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Log; import com.sun.source.tree.ClassTree; import com.sun.source.util.JavacTask; import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; /** * This class is an abstract annotation processor designed to be a * convenient superclass for concrete "type processors", processors that * require the type information in the processed source. * *
Type processing occurs in one round after the tool (e.g. Java compiler) * analyzes the source (all sources taken as input to the tool and sources * generated by other annotation processors). * *
The tool infrastructure will interact with classes extending this abstract * class as follows. * *
* 1-3 are Identical to the {@link Processor} life cycle. * 4-5 are unique to {@code AbstractTypeProcessor} subclasses. * *
The tool is permitted to ask type processors to process a class once * it is analyzed before the rest of classes are analyzed. The tool is also * permitted to stop type processing immediately if any errors are raised, * without invoking {@code typeProcessingOver} * *
A subclass may override any of the methods in this class, as long as the
* general {@link javax.annotation.processing.Processor Processor}
* contract is obeyed, with one notable exception.
* {@link #process(Set, RoundEnvironment)} may not be overridden, as it
* is called during the declaration annotation phase before classes are analyzed.
*
* @author Mahmood Ali
* @author Werner Dietl
*/
public abstract class AbstractTypeProcessor extends AbstractProcessor {
/**
* The set of fully-qualified element names that should be type-checked.
* We store the names of the elements, in order to prevent
* possible confusion between different Element instantiations.
*/
private final Set Subclasses may override this method to do any initialization work.
*/
public void typeProcessingStart() {}
/**
* Processes a fully analyzed class that contains a supported annotation
* (look {@link #getSupportedAnnotationTypes()}).
*
* The passed class is always valid type-checked Java code.
*
* @param element element of the analyzed class
* @param tree the tree path to the element, with the leaf being a
* {@link ClassTree}
*/
public abstract void typeProcess(TypeElement element, TreePath tree);
/**
* A method to be called once all the classes are processed and no error
* is reported.
*
* Subclasses may override this method to do any aggregate analysis
* (e.g. generate report, persistence) or resource deallocation.
*
* If an error (a Java error or a processor error) is reported, this
* method is not guaranteed to be invoked.
*/
public void typeProcessingOver() { }
/**
* A task listener that invokes the processor whenever a class is fully
* analyzed.
*/
private final class AttributionTaskListener implements TaskListener {
@Override
public void finished(TaskEvent e) {
if (e.getKind() != TaskEvent.Kind.ANALYZE)
return;
if (!hasInvokedTypeProcessingStart) {
typeProcessingStart();
hasInvokedTypeProcessingStart = true;
}
Log log = Log.instance(((JavacProcessingEnvironment) processingEnv).getContext());
if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
typeProcessingOver();
hasInvokedTypeProcessingOver = true;
}
if (e.getTypeElement() == null)
throw new AssertionError("event task without a type element");
if (e.getCompilationUnit() == null)
throw new AssertionError("event task without compilation unit");
if (!elements.remove(e.getTypeElement().getQualifiedName()))
return;
TypeElement elem = e.getTypeElement();
TreePath p = Trees.instance(processingEnv).getPath(elem);
typeProcess(elem, p);
if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
typeProcessingOver();
hasInvokedTypeProcessingOver = true;
}
}
@Override
public void started(TaskEvent e) { }
}
}