• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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