• 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.Locale;
9 import java.util.Map;
10 import java.util.Set;
11 import javax.annotation.processing.AbstractProcessor;
12 import javax.annotation.processing.ProcessingEnvironment;
13 import javax.annotation.processing.RoundEnvironment;
14 import javax.annotation.processing.SupportedAnnotationTypes;
15 import javax.annotation.processing.SupportedOptions;
16 import javax.lang.model.SourceVersion;
17 import javax.lang.model.element.Element;
18 import javax.lang.model.element.TypeElement;
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.SdkStore;
29 import org.robolectric.annotation.processing.validator.Validator;
30 
31 /** Annotation processor entry point for Robolectric annotations. */
32 @SupportedOptions({
33   RobolectricProcessor.PACKAGE_OPT,
34   RobolectricProcessor.SHOULD_INSTRUMENT_PKG_OPT
35 })
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 JSON_DOCS_ENABLED = "org.robolectric.annotation.processing.jsonDocsEnabled";
43   static final String SDK_CHECK_MODE = "org.robolectric.annotation.processing.sdkCheckMode";
44   private static final String SDKS_FILE = "org.robolectric.annotation.processing.sdks";
45   private static final String DISABLE_INDEVELOPMENT =
46       "org.robolectric.annotation.processing.disableInDevelopment";
47   private static final String ALLOW_LOOSE_SIGNATURES =
48       "org.robolectric.annotation.processing.allowLooseSignatures";
49 
50   /** required for Android Development. */
51   private static final String VALIDATE_COMPILE_SDKS =
52       "org.robolectric.annotation.processing.validateCompileSdk";
53 
54   private static final String PRIORITY = "org.robolectric.annotation.processing.priority";
55 
56   private RobolectricModel.Builder modelBuilder;
57   private String shadowPackage;
58   private boolean shouldInstrumentPackages;
59   private int priority;
60   private ImplementsValidator.SdkCheckMode sdkCheckMode;
61   private String sdksFile;
62   private Map<String, String> options;
63   private boolean generated = false;
64   private final List<Generator> generators = new ArrayList<>();
65   private final Map<TypeElement, Validator> elementValidators = new HashMap<>(13);
66   private File jsonDocsDir;
67   private boolean jsonDocsEnabled;
68   private boolean validateCompiledSdk;
69   private boolean allowInDev;
70   private String overrideSdkLocation;
71   private int overrideSdkInt;
72   private boolean allowLooseSignatures;
73 
74   /** Default constructor. */
RobolectricProcessor()75   public RobolectricProcessor() {}
76 
77   /**
78    * Constructor to use for testing passing options in. Only necessary until compile-testing
79    * supports passing options in.
80    *
81    * @param options simulated options that would ordinarily be passed in the {@link
82    *     ProcessingEnvironment}.
83    */
84   @VisibleForTesting
RobolectricProcessor(Map<String, String> options)85   public RobolectricProcessor(Map<String, String> options) {
86     this(options, null, -1);
87   }
88 
RobolectricProcessor( Map<String, String> options, String overrideSdkLocation, int overrideSdkInt)89   public RobolectricProcessor(
90       Map<String, String> options, String overrideSdkLocation, int overrideSdkInt) {
91     processOptions(options);
92     this.overrideSdkLocation = overrideSdkLocation;
93     this.overrideSdkInt = overrideSdkInt;
94   }
95 
96   @Override
init(ProcessingEnvironment environment)97   public synchronized void init(ProcessingEnvironment environment) {
98     super.init(environment);
99     processOptions(environment.getOptions());
100     modelBuilder = new RobolectricModel.Builder(environment);
101 
102     SdkStore sdkStore =
103         new SdkStore(sdksFile, validateCompiledSdk, overrideSdkLocation, overrideSdkInt);
104 
105     addValidator(new ImplementationValidator(modelBuilder, environment));
106     addValidator(
107         new ImplementsValidator(
108             modelBuilder, environment, sdkCheckMode, sdkStore, allowInDev, allowLooseSignatures));
109     addValidator(new RealObjectValidator(modelBuilder, environment));
110     addValidator(new ResetterValidator(modelBuilder, environment));
111   }
112 
113   @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)114   public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
115     for (TypeElement annotation : annotations) {
116       Validator validator = elementValidators.get(annotation);
117       if (validator != null) {
118         for (Element elem : roundEnv.getElementsAnnotatedWith(annotation)) {
119           validator.visit(elem, elem.getEnclosingElement());
120         }
121       }
122     }
123 
124     if (!generated) {
125       RobolectricModel model = modelBuilder.build();
126 
127       generators.add(
128           new ShadowProviderGenerator(
129               model, processingEnv, shadowPackage, shouldInstrumentPackages, priority));
130       generators.add(new ServiceLoaderGenerator(processingEnv, shadowPackage));
131       if (jsonDocsEnabled) {
132         generators.add(new JavadocJsonGenerator(model, processingEnv, jsonDocsDir));
133       }
134       for (Generator generator : generators) {
135         generator.generate();
136       }
137       generated = true;
138     }
139     return false;
140   }
141 
addValidator(Validator v)142   private void addValidator(Validator v) {
143     elementValidators.put(v.getAnnotationType(), v);
144   }
145 
processOptions(Map<String, String> options)146   private void processOptions(Map<String, String> options) {
147     if (this.options == null) {
148       this.options = options;
149       this.shadowPackage = options.get(PACKAGE_OPT);
150       this.shouldInstrumentPackages =
151           !"false".equalsIgnoreCase(options.get(SHOULD_INSTRUMENT_PKG_OPT));
152       this.jsonDocsDir = new File(options.getOrDefault(JSON_DOCS_DIR, "build/docs/json"));
153       this.jsonDocsEnabled = "true".equalsIgnoreCase(options.get(JSON_DOCS_ENABLED));
154       this.sdkCheckMode =
155           SdkCheckMode.valueOf(
156               options.getOrDefault(SDK_CHECK_MODE, "WARN").toUpperCase(Locale.ROOT));
157       this.validateCompiledSdk =
158           "true".equalsIgnoreCase(options.getOrDefault(VALIDATE_COMPILE_SDKS, "false"));
159       this.sdksFile = getSdksFile(options, SDKS_FILE);
160       this.priority = Integer.parseInt(options.getOrDefault(PRIORITY, "0"));
161       this.allowInDev =
162           !"true".equalsIgnoreCase(options.getOrDefault(DISABLE_INDEVELOPMENT, "false"));
163       this.allowLooseSignatures =
164           "true".equalsIgnoreCase(options.getOrDefault(ALLOW_LOOSE_SIGNATURES, "false"));
165       if (this.shadowPackage == null) {
166         throw new IllegalArgumentException("no package specified for " + PACKAGE_OPT);
167       }
168     }
169   }
170 
171   /**
172    * Extendable to support Bazel environments, where the sdks file is generated as a build artifact.
173    */
getSdksFile(Map<String, String> options, String sdksFileParam)174   protected String getSdksFile(Map<String, String> options, String sdksFileParam) {
175     return options.get(sdksFileParam);
176   }
177 
178   @Override
getSupportedSourceVersion()179   public SourceVersion getSupportedSourceVersion() {
180     return SourceVersion.latest();
181   }
182 }
183