package io.takari.maven.plugins.compile.jdt;
import java.util.Iterator;
import java.util.ServiceLoader;
import javax.annotation.processing.Processor;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseAnnotationProcessorManager;
import org.eclipse.jdt.internal.compiler.apt.dispatch.ProcessorInfo;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.takari.incrementalbuild.MessageSeverity;
import io.takari.maven.plugins.compile.CompilerBuildContext;
// TODO reconcile with BatchAnnotationProcessorManager
class AnnotationProcessorManager extends BaseAnnotationProcessorManager {
private Logger logger = LoggerFactory.getLogger(getClass());
private final CompilerBuildContext context;
private static interface ResettableProcessorIterator extends Iterator<Processor> {
void reset();
}
private final ResettableProcessorIterator processors;
private boolean finished;
private static class SpecifiedProcessors implements ResettableProcessorIterator {
private final ClassLoader loader;
private final String[] processors;
private int idx;
public SpecifiedProcessors(ClassLoader loader, String[] processors) {
this.loader = loader;
this.processors = processors;
}
@Override
public boolean hasNext() {
return idx < processors.length;
}
@Override
public Processor next() {
try {
return (Processor) loader.loadClass(processors[idx++]).newInstance();
} catch (ReflectiveOperationException e) {
// TODO: better error handling
throw new AbortCompilation(null, e);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void reset() {
idx = 0;
}
}
private static class DiscoveredProcessors implements ResettableProcessorIterator {
private final ServiceLoader<Processor> loader;
private Iterator<Processor> iterator;
public DiscoveredProcessors(ClassLoader procLoader) {
this.loader = ServiceLoader.load(Processor.class, procLoader);
this.iterator = loader.iterator();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Processor next() {
return iterator.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void reset() {
this.iterator = loader.iterator();
}
}
public AnnotationProcessorManager(CompilerBuildContext context, ProcessingEnvImpl processingEnv, StandardJavaFileManager fileManager, String[] processors) {
this.context = context;
this._processingEnv = processingEnv;
ClassLoader procLoader = fileManager.getClassLoader(StandardLocation.ANNOTATION_PROCESSOR_PATH);
this.processors = processors != null //
? new SpecifiedProcessors(procLoader, processors) //
: new DiscoveredProcessors(procLoader);
}
@Override
public ProcessorInfo discoverNextProcessor() {
if (processors.hasNext()) {
Processor processor = processors.next();
processor.init(_processingEnv);
ProcessorInfo procecssorInfo = new ProcessorInfo(processor);
_processors.add(procecssorInfo); // TODO this needs to happen in RoundDispatcher.round()
return procecssorInfo;
}
return null;
}
@Override
public void reportProcessorException(Processor p, Exception e) {
String msg = String.format("Exception executing annotation processor %s: %s", p.getClass().getName(), e.getMessage());
context.addPomMessage(msg, MessageSeverity.ERROR, e);
throw new AbortCompilation(null, e);
}
@Override
public void processAnnotations(CompilationUnitDeclaration[] units, ReferenceBinding[] referenceBindings, boolean isLastRound) {
if (finished) {
// workaround eclipse jdt compiler bug 468893
logger.warn("Suppressed duplicate processingOver==true annotation processor invocation, see Eclipse bug 468893");
return;
}
finished = isLastRound;
super.processAnnotations(units, referenceBindings, isLastRound);
}
/**
* Resets this annotation processor manager between incremental compiler loop iterations.
*/
public void hardReset() {
// clear/reset parent state
((ProcessingEnvImpl) _processingEnv).hardReset();
_processors.clear();
_isFirstRound = true;
_round = 0;
// clear/reset this class state
processors.reset();
finished = false;
}
}