1 package org.robolectric.annotation.processing; 2 3 import com.google.common.annotations.VisibleForTesting; 4 import java.io.File; 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.List; 8 import java.util.Map; 9 import java.util.Set; 10 import javax.annotation.processing.AbstractProcessor; 11 import javax.annotation.processing.ProcessingEnvironment; 12 import javax.annotation.processing.RoundEnvironment; 13 import javax.annotation.processing.SupportedAnnotationTypes; 14 import javax.annotation.processing.SupportedOptions; 15 import javax.lang.model.SourceVersion; 16 import javax.lang.model.element.Element; 17 import javax.lang.model.element.TypeElement; 18 import org.robolectric.annotation.processing.RobolectricModel.Builder; 19 import org.robolectric.annotation.processing.generator.Generator; 20 import org.robolectric.annotation.processing.generator.JavadocJsonGenerator; 21 import org.robolectric.annotation.processing.generator.ServiceLoaderGenerator; 22 import org.robolectric.annotation.processing.generator.ShadowProviderGenerator; 23 import org.robolectric.annotation.processing.validator.ImplementationValidator; 24 import org.robolectric.annotation.processing.validator.ImplementsValidator; 25 import org.robolectric.annotation.processing.validator.ImplementsValidator.SdkCheckMode; 26 import org.robolectric.annotation.processing.validator.RealObjectValidator; 27 import org.robolectric.annotation.processing.validator.ResetterValidator; 28 import org.robolectric.annotation.processing.validator.Validator; 29 30 /** 31 * Annotation processor entry point for Robolectric annotations. 32 */ 33 @SupportedOptions({ 34 RobolectricProcessor.PACKAGE_OPT, 35 RobolectricProcessor.SHOULD_INSTRUMENT_PKG_OPT}) 36 @SupportedAnnotationTypes("org.robolectric.annotation.*") 37 public class RobolectricProcessor extends AbstractProcessor { 38 static final String PACKAGE_OPT = "org.robolectric.annotation.processing.shadowPackage"; 39 static final String SHOULD_INSTRUMENT_PKG_OPT = 40 "org.robolectric.annotation.processing.shouldInstrumentPackage"; 41 static final String JSON_DOCS_DIR = "org.robolectric.annotation.processing.jsonDocsDir"; 42 static final String SDK_CHECK_MODE = 43 "org.robolectric.annotation.processing.sdkCheckMode"; 44 45 private Builder modelBuilder; 46 private String shadowPackage; 47 private boolean shouldInstrumentPackages; 48 private ImplementsValidator.SdkCheckMode sdkCheckMode; 49 private Map<String, String> options; 50 private boolean generated = false; 51 private final List<Generator> generators = new ArrayList<>(); 52 private final Map<TypeElement, Validator> elementValidators = new HashMap<>(13); 53 private File jsonDocsDir; 54 55 /** 56 * Default constructor. 57 */ RobolectricProcessor()58 public RobolectricProcessor() { 59 } 60 61 /** 62 * Constructor to use for testing passing options in. Only 63 * necessary until compile-testing supports passing options 64 * in. 65 * 66 * @param options simulated options that would ordinarily 67 * be passed in the {@link ProcessingEnvironment}. 68 */ 69 @VisibleForTesting RobolectricProcessor(Map<String, String> options)70 public RobolectricProcessor(Map<String, String> options) { 71 processOptions(options); 72 } 73 74 @Override init(ProcessingEnvironment environment)75 public synchronized void init(ProcessingEnvironment environment) { 76 super.init(environment); 77 processOptions(environment.getOptions()); 78 modelBuilder = new Builder(environment); 79 80 addValidator(new ImplementationValidator(modelBuilder, environment)); 81 addValidator(new ImplementsValidator(modelBuilder, environment, sdkCheckMode)); 82 addValidator(new RealObjectValidator(modelBuilder, environment)); 83 addValidator(new ResetterValidator(modelBuilder, environment)); 84 } 85 86 @Override process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)87 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 88 for (TypeElement annotation : annotations) { 89 Validator validator = elementValidators.get(annotation); 90 if (validator != null) { 91 for (Element elem : roundEnv.getElementsAnnotatedWith(annotation)) { 92 validator.visit(elem, elem.getEnclosingElement()); 93 } 94 } 95 } 96 97 if (!generated) { 98 RobolectricModel model = modelBuilder.build(); 99 100 generators.add(new ShadowProviderGenerator(model, processingEnv, shadowPackage, shouldInstrumentPackages)); 101 generators.add(new ServiceLoaderGenerator(processingEnv, shadowPackage)); 102 generators.add(new JavadocJsonGenerator(model, processingEnv, jsonDocsDir)); 103 104 for (Generator generator : generators) { 105 generator.generate(); 106 } 107 generated = true; 108 } 109 return true; 110 } 111 addValidator(Validator v)112 private void addValidator(Validator v) { 113 elementValidators.put(v.getAnnotationType(), v); 114 } 115 processOptions(Map<String, String> options)116 private void processOptions(Map<String, String> options) { 117 if (this.options == null) { 118 this.options = options; 119 this.shadowPackage = options.get(PACKAGE_OPT); 120 this.shouldInstrumentPackages = 121 !"false".equalsIgnoreCase(options.get(SHOULD_INSTRUMENT_PKG_OPT)); 122 jsonDocsDir = new File(options.getOrDefault(JSON_DOCS_DIR, "build/docs/json")); 123 this.sdkCheckMode = 124 SdkCheckMode.valueOf(options.getOrDefault(SDK_CHECK_MODE, "WARN").toUpperCase()); 125 126 if (this.shadowPackage == null) { 127 throw new IllegalArgumentException("no package specified for " + PACKAGE_OPT); 128 } 129 } 130 } 131 132 @Override getSupportedSourceVersion()133 public SourceVersion getSupportedSourceVersion() { 134 return SourceVersion.latest(); 135 } 136 } 137