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