package org.robolectric.annotation.processing;
import org.robolectric.annotation.processing.generator.Generator;
import org.robolectric.annotation.processing.generator.JavadocJsonGenerator;
import org.robolectric.annotation.processing.generator.ServiceLoaderGenerator;
import org.robolectric.annotation.processing.generator.ShadowProviderGenerator;
import org.robolectric.annotation.processing.validator.ImplementationValidator;
import org.robolectric.annotation.processing.validator.ImplementsValidator;
import org.robolectric.annotation.processing.validator.RealObjectValidator;
import org.robolectric.annotation.processing.validator.ResetterValidator;
import org.robolectric.annotation.processing.validator.Validator;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Annotation processor entry point for Robolectric annotations.
*/
@SupportedOptions({
RobolectricProcessor.PACKAGE_OPT,
RobolectricProcessor.SHOULD_INSTRUMENT_PKG_OPT})
@SupportedAnnotationTypes("org.robolectric.annotation.*")
public class RobolectricProcessor extends AbstractProcessor {
static final String PACKAGE_OPT = "org.robolectric.annotation.processing.shadowPackage";
static final String SHOULD_INSTRUMENT_PKG_OPT =
"org.robolectric.annotation.processing.shouldInstrumentPackage";
private RobolectricModel model;
private String shadowPackage;
private boolean shouldInstrumentPackages;
private Map<String, String> options;
private boolean generated = false;
private final List<Generator> generators = new ArrayList<>();
private final Map<TypeElement, Validator> elementValidators = new HashMap<>(13);
/**
* Default constructor.
*/
public RobolectricProcessor() {
}
/**
* Constructor to use for testing passing options in. Only
* necessary until compile-testing supports passing options
* in.
*
* @param options simulated options that would ordinarily
* be passed in the {@link ProcessingEnvironment}.
*/
RobolectricProcessor(Map<String, String> options) {
processOptions(options);
}
@Override
public void init(ProcessingEnvironment environment) {
super.init(environment);
processOptions(environment.getOptions());
model = new RobolectricModel(environment.getElementUtils(), environment.getTypeUtils());
addValidator(new ImplementationValidator(model, environment));
addValidator(new ImplementsValidator(model, environment));
addValidator(new RealObjectValidator(model, environment));
addValidator(new ResetterValidator(model, environment));
generators.add(new ShadowProviderGenerator(model, environment, shadowPackage, shouldInstrumentPackages));
generators.add(new ServiceLoaderGenerator(environment, shadowPackage));
generators.add(new JavadocJsonGenerator(model, environment));
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
Validator validator = elementValidators.get(annotation);
if (validator != null) {
for (Element elem : roundEnv.getElementsAnnotatedWith(annotation)) {
validator.visit(elem, elem.getEnclosingElement());
}
}
}
if (!generated) {
model.prepare();
for (Generator generator : generators) {
generator.generate();
}
generated = true;
}
return true;
}
private void addValidator(Validator v) {
elementValidators.put(v.getAnnotationType(), v);
}
private void processOptions(Map<String, String> options) {
if (this.options == null) {
this.options = options;
this.shadowPackage = options.get(PACKAGE_OPT);
this.shouldInstrumentPackages =
!"false".equalsIgnoreCase(options.get(SHOULD_INSTRUMENT_PKG_OPT));
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}