• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.enterprise.connectedapps.processor;
17 
18 import static com.google.android.enterprise.connectedapps.processor.CommonClassNames.PARCELABLE_CREATOR_CLASSNAME;
19 import static com.google.android.enterprise.connectedapps.processor.GeneratorUtilities.findCrossProfileMethodsInClass;
20 import static com.google.android.enterprise.connectedapps.processor.TypeUtils.extractTypeArguments;
21 import static com.google.android.enterprise.connectedapps.processor.TypeUtils.getRawTypeClassName;
22 import static com.google.android.enterprise.connectedapps.processor.TypeUtils.getRawTypeQualifiedName;
23 import static com.google.android.enterprise.connectedapps.processor.TypeUtils.removeTypeArguments;
24 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.hasCrossProfileAnnotation;
25 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.hasCrossProfileConfigurationAnnotation;
26 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.hasCrossProfileConfigurationsAnnotation;
27 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.hasCrossProfileProviderAnnotation;
28 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.validationMessageFormatterFor;
29 import static com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder.validationMessageFormatterForClass;
30 import static java.util.stream.Collectors.toMap;
31 import static java.util.stream.Collectors.toSet;
32 
33 import com.google.android.enterprise.connectedapps.annotations.Cacheable;
34 import com.google.android.enterprise.connectedapps.annotations.CustomFutureWrapper;
35 import com.google.android.enterprise.connectedapps.annotations.CustomParcelableWrapper;
36 import com.google.android.enterprise.connectedapps.annotations.CustomProfileConnector;
37 import com.google.android.enterprise.connectedapps.annotations.CustomUserConnector;
38 import com.google.android.enterprise.connectedapps.processor.SupportedTypes.TypeCheckContext;
39 import com.google.android.enterprise.connectedapps.processor.annotationdiscovery.AnnotationFinder;
40 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileAnnotationInfo;
41 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileCallbackAnnotationInfo;
42 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileMethodInfo;
43 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileProviderAnnotationInfo;
44 import com.google.android.enterprise.connectedapps.processor.containers.FutureWrapperAnnotationInfo;
45 import com.google.android.enterprise.connectedapps.processor.containers.ParcelableWrapperAnnotationInfo;
46 import com.google.android.enterprise.connectedapps.processor.containers.ProfileConnectorInfo;
47 import com.google.android.enterprise.connectedapps.processor.containers.UserConnectorInfo;
48 import com.google.android.enterprise.connectedapps.processor.containers.ValidatorContext;
49 import com.google.android.enterprise.connectedapps.processor.containers.ValidatorCrossProfileConfigurationInfo;
50 import com.google.android.enterprise.connectedapps.processor.containers.ValidatorCrossProfileTestInfo;
51 import com.google.android.enterprise.connectedapps.processor.containers.ValidatorCrossProfileTypeInfo;
52 import com.google.android.enterprise.connectedapps.processor.containers.ValidatorProviderClassInfo;
53 import com.squareup.javapoet.ClassName;
54 import com.squareup.javapoet.ParameterizedTypeName;
55 import com.squareup.javapoet.TypeName;
56 import com.google.common.collect.ImmutableList;
57 import java.io.Serializable;
58 import java.util.Arrays;
59 import java.util.Collection;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Objects;
64 import java.util.Optional;
65 import java.util.stream.Collectors;
66 import java.util.stream.Stream;
67 import javax.lang.model.element.Element;
68 import javax.lang.model.element.ElementKind;
69 import javax.lang.model.element.ExecutableElement;
70 import javax.lang.model.element.Modifier;
71 import javax.lang.model.element.PackageElement;
72 import javax.lang.model.element.TypeElement;
73 import javax.lang.model.element.VariableElement;
74 import javax.lang.model.type.TypeKind;
75 import javax.lang.model.type.TypeMirror;
76 import javax.lang.model.util.ElementFilter;
77 import javax.tools.Diagnostic.Kind;
78 
79 /** Validator to check that annotations have been used correctly before generating code. */
80 public final class EarlyValidator {
81 
82   private static final String MULTIPLE_PROVIDERS_ERROR =
83       "The @CROSS_PROFILE_ANNOTATION annotated type %s has been provided more than once";
84   private static final String PROVIDING_NON_CROSS_PROFILE_TYPE_ERROR =
85       "Methods annotated @CROSS_PROFILE_PROVIDER_ANNOTATION must only return"
86           + " @CROSS_PROFILE_ANNOTATION annotated types";
87   private static final String INVALID_CONSTRUCTORS_ERROR =
88       "Provider classes must have a single public constructor which takes either a single Context"
89           + " argument or no arguments";
90   private static final String PROVIDER_INCORRECT_ARGS_ERROR =
91       "Methods annotated @CROSS_PROFILE_PROVIDER_ANNOTATION can only take a single Context"
92           + " argument, or no-args";
93   private static final String STATIC_PROVIDER_ERROR =
94       "Methods annotated @CROSS_PROFILE_PROVIDER_ANNOTATION can not be static";
95   private static final String UNSUPPORTED_RETURN_TYPE_ERROR =
96       "The type %s cannot be returned by methods annotated @CROSS_PROFILE_ANNOTATION";
97   private static final String UNSUPPORTED_PARAMETER_TYPE_CROSS_PROFILE_METHOD =
98       "The type %s cannot be used by parameters of methods annotated @CROSS_PROFILE_ANNOTATION";
99   private static final String UNSUPPORTED_PARAMETER_TYPE_CROSS_ASYNC_CALLBACK =
100       "The type %s cannot be used by parameters of methods on interfaces annotated"
101           + " @CROSS_PROFILE_CALLBACK_ANNOTATION";
102   private static final String CROSS_PROFILE_TYPE_DEFAULT_PACKAGE_ERROR =
103       "@CROSS_PROFILE_ANNOTATION types must not be in the default package";
104   private static final String NON_PUBLIC_CROSS_PROFILE_TYPE_ERROR =
105       "@CROSS_PROFILE_ANNOTATION types must be public";
106   private static final String ADDITIONAL_TYPE_INVALID_TYPE_ERROR =
107       "The additional type %s cannot be used by used as a parameter for, or returned by methods"
108           + " annotated @CROSS_PROFILE_ANNOTATION";
109   private static final String NOT_A_PROVIDER_CLASS_ERROR =
110       "All classes specified in 'providers' must be provider classes";
111   private static final String CONNECTOR_MUST_BE_INTERFACE = "Connectors must be interfaces";
112   private static final String CONNECTOR_MUST_EXTEND_CONNECTOR =
113       "Interfaces specified as a connector must extend ProfileConnector or UserConnector";
114   private static final String CUSTOM_PROFILE_CONNECTOR_MUST_BE_INTERFACE =
115       "@CustomProfileConnector must only be applied to interfaces";
116   private static final String CUSTOM_USER_CONNECTOR_MUST_BE_INTERFACE =
117       "@CustomUserConnector must only be applied to interfaces";
118   private static final String GENERATED_PROFILE_CONNECTOR_MUST_BE_INTERFACE =
119       "@GeneratedProfileConnector must only be applied to interfaces";
120   private static final String GENERATED_USER_CONNECTOR_MUST_BE_INTERFACE =
121       "@GeneratedUserConnector must only be applied to interfaces";
122   private static final String CUSTOM_PROFILE_CONNECTOR_MUST_EXTEND_CONNECTOR =
123       "Interfaces annotated with @CustomProfileConnector must extend ProfileConnector";
124   private static final String CUSTOM_USER_CONNECTOR_MUST_EXTEND_CONNECTOR =
125       "Interfaces annotated with @CustomUserConnector must extend UserConnector";
126   private static final String GENERATED_PROFILE_CONNECTOR_MUST_EXTEND_PROFILE_CONNECTOR =
127       "Interfaces annotated with @GeneratedProfileConnector must extend ProfileConnector";
128   private static final String GENERATED_USER_CONNECTOR_MUST_EXTEND_USER_CONNECTOR =
129       "Interfaces annotated with @GeneratedUserConnector must extend UserConnector";
130   private static final String CALLBACK_INTERFACE_DEFAULT_PACKAGE_ERROR =
131       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION must not be in the default package";
132   private static final String NOT_INTERFACE_ERROR =
133       "Only interfaces may be annotated @CROSS_PROFILE_CALLBACK_ANNOTATION";
134   private static final String NOT_ONE_METHOD_ERROR =
135       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION(simple=true) must have exactly one"
136           + " method";
137   private static final String NO_METHODS_ERROR =
138       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION must have at least one method";
139   private static final String DEFAULT_METHOD_ERROR =
140       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION must have no default methods";
141   private static final String STATIC_METHOD_ERROR =
142       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION must have no static methods";
143   private static final String NOT_VOID_ERROR =
144       "Methods on interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION must return void";
145   private static final String GENERIC_CALLBACK_INTERFACE_ERROR =
146       "Interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION can not be generic";
147   private static final String MORE_THAN_ONE_PARAMETER_ERROR =
148       "Methods on interfaces annotated @CROSS_PROFILE_CALLBACK_ANNOTATION(simple=true) can only"
149           + " take a single parameter";
150   private static final String MULTIPLE_ASYNC_CALLBACK_PARAMETERS_ERROR =
151       "Methods annotated @CROSS_PROFILE_ANNOTATION can have a maximum of one parameter of a type"
152           + " annotated @CROSS_PROFILE_CALLBACK_ANNOTATION";
153   private static final String NON_VOID_CALLBACK_ERROR =
154       "Methods annotated @CROSS_PROFILE_ANNOTATION which take a parameter type annotated"
155           + " @CROSS_PROFILE_CALLBACK_ANNOTATION must return void";
156   private static final String METHOD_ISSTATIC_ERROR =
157       "@CROSS_PROFILE_ANNOTATION annotations on methods can not specify isStatic";
158   private static final String METHOD_CONNECTOR_ERROR =
159       "@CROSS_PROFILE_ANNOTATION annotations on methods can not specify a connector";
160   private static final String METHOD_PARCELABLE_WRAPPERS_ERROR =
161       "@CROSS_PROFILE_ANNOTATION annotations on methods can not specify parcelable wrappers";
162   private static final String ADDITIONAL_PROFILE_CONNECTOR_METHODS_ERROR =
163       "Interfaces annotated with @GeneratedProfileConnector can not declare non-static methods";
164   private static final String ADDITIONAL_USER_CONNECTOR_METHODS_ERROR =
165       "Interfaces annotated with @GeneratedUserConnector can not declare non-static methods";
166   private static final String NOT_A_CONFIGURATION_ERROR =
167       "Configurations referenced in a @CROSS_PROFILE_TEST_ANNOTATION annotation must be annotated"
168           + " @CROSS_PROFILE_CONFIGURATION_ANNOTATION or @CROSS_PROFILE_CONFIGURATIONS_ANNOTATION";
169   private static final String ASYNC_DECLARED_EXCEPTION_ERROR =
170       "Asynchronous methods annotated @CROSS_PROFILE_ANNOTATION cannot declare exceptions";
171   private static final String NOT_PARCELABLE_ERROR =
172       "Classes annotated @CustomParcelableWrapper must implement Parcelable";
173   private static final String INCORRECT_OF_METHOD =
174       "Classes annotated @CustomParcelableWrapper must have a static 'of' method which takes a"
175           + " Bundler, a BundlerType, and an instance of the wrapped type as arguments and returns"
176           + " an instance of the parcelable wrapper";
177   private static final String INCORRECT_GET_METHOD =
178       "Classes annotated @CustomParcelableWrapper must have a static 'get' method which takes no"
179           + " arguments and returns an instance of the wrapped class";
180   private static final String INCORRECT_PARCELABLE_IMPLEMENTATION =
181       "Classes annotated @CustomParcelableWrapper must correctly implement Parcelable";
182   private static final String PARCELABLE_WRAPPER_ANNOTATION_ERROR =
183       "Parcelable Wrappers must be annotated @CustomParcelableWrapper";
184   private static final String DOES_NOT_EXTEND_FUTURE_WRAPPER_ERROR =
185       "Classes annotated @CustomFutureWrapper must extend FutureWrapper";
186   private static final String INCORRECT_CREATE_METHOD_ERROR =
187       "Classes annotated @CustomFutureWrapper must have a create method which returns an instance"
188           + " of the class and takes a Bundler and BundlerType argument";
189   private static final String INCORRECT_GET_FUTURE_METHOD_ERROR =
190       "Classes annotated @CustomFutureWrapper must have a getFuture method which returns an"
191           + " instance of the wrapped future and takes no arguments";
192   private static final String INCORRECT_RESOLVE_CALLBACK_WHEN_FUTURE_IS_SET_METHOD_ERROR =
193       "Classes annotated @CustomFutureWrapper must have a writeFutureResult method"
194           + " which returns void and takes as arguments an instance of the wrapped future and a"
195           + " FutureResultWriter";
196   private static final String INCORRECT_GROUP_RESULTS_METHOD_ERROR =
197       "Classes annotated @CustomFutureWrapper must have a groupResults method which returns an"
198           + " instance of the wrapped future containing a map from Profile to the wrapped future"
199           + " type, and takes as an argument a map from Profile to an instance of the wrapped"
200           + " future";
201   private static final String FUTURE_WRAPPER_ANNOTATION_ERROR =
202       "Future Wrappers must be annotated @CustomFutureWrapper";
203   private static final String IMPORTS_NOT_PROFILE_CONNECTOR_ERROR =
204       "Classes included in includes= must be annotated @CustomProfileConnector";
205   private static final String IMPORTS_NOT_USER_CONNECTOR_ERROR =
206       "Classes included in includes= must be annotated @CustomUserConnector";
207   private static final String MUST_HAVE_ONE_TYPE_PARAMETER_ERROR =
208       "Classes annotated @CustomFutureWrapper must have a single type parameter";
209   private static final String NOT_STATIC_ERROR =
210       "Types annotated @CROSS_PROFILE_ANNOTATION(isStatic=true) must not contain any non-static"
211           + " methods annotated @CROSS_PROFILE_ANNOTATION";
212   private static final String METHOD_STATICTYPES_ERROR =
213       "@CROSS_PROFILE_PROVIDER_ANNOTATION annotations on methods can not specify staticTypes";
214   private static final String CACHEABLE_METHOD_RETURNS_VOID_ERROR =
215       "Methods annotated with @Cacheable must return a non-void type";
216   private static final String CACHEABLE_METHOD_RETURNS_NON_SERIALIZABLE_ERROR =
217       "Methods annotated with @Cacheable must return a type which implements Serializable, return"
218           + " a future with a Serializable result or return void with a simple callback parameter.";
219   private static final String CACHEABLE_METHOD_NON_SIMPLE_CALLBACK_ERROR =
220       "Methods annotated with @Cacheable may only have a callback parameter which is simple.";
221   private static final String CACHEABLE_METHOD_USES_INVALID_PARAMETERS_ERROR =
222       "Methods annotated with @Cacheable may only use callbacks that take a single Serializable"
223           + " parameter.";
224 
225   private final ValidatorContext validatorContext;
226   private final TypeMirror contextType;
227   private final TypeMirror profileConnectorType;
228   private final TypeMirror userConnectorType;
229   private final TypeMirror parcelableType;
230   private final TypeMirror bundlerType;
231   private final TypeMirror bundlerTypeType;
232   private final TypeMirror futureResultWriterType;
233   private final TypeMirror profileType;
234 
EarlyValidator(ValidatorContext validatorContext)235   EarlyValidator(ValidatorContext validatorContext) {
236     this.validatorContext = validatorContext;
237     contextType = validatorContext.elements().getTypeElement("android.content.Context").asType();
238 
239     parcelableType = validatorContext.elements().getTypeElement("android.os.Parcelable").asType();
240 
241     profileConnectorType =
242         validatorContext
243             .elements()
244             .getTypeElement("com.google.android.enterprise.connectedapps.ProfileConnector")
245             .asType();
246 
247     userConnectorType =
248         validatorContext
249             .elements()
250             .getTypeElement("com.google.android.enterprise.connectedapps.UserConnector")
251             .asType();
252 
253     bundlerType =
254         validatorContext
255             .elements()
256             .getTypeElement("com.google.android.enterprise.connectedapps.internal.Bundler")
257             .asType();
258 
259     bundlerTypeType =
260         validatorContext
261             .elements()
262             .getTypeElement("com.google.android.enterprise.connectedapps.internal.BundlerType")
263             .asType();
264 
265     futureResultWriterType =
266         validatorContext
267             .elements()
268             .getTypeElement(
269                 "com.google.android.enterprise.connectedapps.internal.FutureResultWriter")
270             .asType();
271 
272     profileType =
273         validatorContext
274             .elements()
275             .getTypeElement("com.google.android.enterprise.connectedapps.Profile")
276             .asType();
277   }
278 
279   /**
280    * Validate code.
281    *
282    * <p>This will show errors for all issues found. It will not terminate upon finding the first
283    * error.
284    *
285    * @return True if the code is valid
286    */
validate()287   boolean validate() {
288 
289     return Stream.of(
290             validateProfileConnectorInterfaces(validatorContext.newProfileConnectorInterfaces()),
291             validateUserConnectorInterfaces(validatorContext.newUserConnectorInterfaces()),
292             validateGeneratedProfileConnectors(validatorContext.newGeneratedProfileConnectors()),
293             validateGeneratedUserConnectors(validatorContext.newGeneratedUserConnectors()),
294             validateConfigurations(validatorContext.newConfigurations()),
295             validateCrossProfileTypes(validatorContext.newCrossProfileTypes()),
296             validateProviderMethods(validatorContext.newProviderMethods()),
297             validateProviderClasses(validatorContext.newProviderClasses()),
298             validateCrossProfileCallbackInterfaces(
299                 validatorContext.newCrossProfileCallbackInterfaces()),
300             validateCrossProfileTests(validatorContext.newCrossProfileTests()),
301             validateCustomParcelableWrappers(validatorContext.newCustomParcelableWrappers()),
302             validateCustomFutureWrappers(validatorContext.newCustomFutureWrappers()))
303         .allMatch(b -> b);
304   }
305 
validateProfileConnectorInterfaces( Collection<ProfileConnectorInfo> connectorInterfaces)306   private boolean validateProfileConnectorInterfaces(
307       Collection<ProfileConnectorInfo> connectorInterfaces) {
308     boolean isValid = true;
309 
310     for (ProfileConnectorInfo connectorInterface : connectorInterfaces) {
311       isValid = validateProfileConnectorInterface(connectorInterface) && isValid;
312     }
313 
314     return isValid;
315   }
316 
validateProfileConnectorInterface(ProfileConnectorInfo connectorInterface)317   private boolean validateProfileConnectorInterface(ProfileConnectorInfo connectorInterface) {
318     boolean isValid = true;
319 
320     if (!connectorInterface.connectorElement().getKind().equals(ElementKind.INTERFACE)) {
321       showError(CUSTOM_PROFILE_CONNECTOR_MUST_BE_INTERFACE, connectorInterface.connectorElement());
322       isValid = false;
323     }
324 
325     if (!implementsInterface(connectorInterface.connectorElement(), profileConnectorType)) {
326       showError(
327           CUSTOM_PROFILE_CONNECTOR_MUST_EXTEND_CONNECTOR, connectorInterface.connectorElement());
328       isValid = false;
329     }
330 
331     for (TypeElement parcelableWrapper : connectorInterface.parcelableWrapperClasses()) {
332       if (parcelableWrapper.getAnnotation(CustomParcelableWrapper.class) == null) {
333         showError(PARCELABLE_WRAPPER_ANNOTATION_ERROR, connectorInterface.connectorElement());
334       }
335     }
336 
337     for (TypeElement futureWrapper : connectorInterface.futureWrapperClasses()) {
338       if (futureWrapper.getAnnotation(CustomFutureWrapper.class) == null) {
339         isValid = false;
340         showError(FUTURE_WRAPPER_ANNOTATION_ERROR, connectorInterface.connectorElement());
341       }
342     }
343 
344     for (TypeElement importer : connectorInterface.importsClasses()) {
345       if (importer.getAnnotation(CustomProfileConnector.class) == null) {
346         isValid = false;
347         showError(IMPORTS_NOT_PROFILE_CONNECTOR_ERROR, connectorInterface.connectorElement());
348         showError(IMPORTS_NOT_PROFILE_CONNECTOR_ERROR, connectorInterface.connectorElement());
349       }
350     }
351 
352     return isValid;
353   }
354 
validateUserConnectorInterfaces( Collection<UserConnectorInfo> connectorInterfaces)355   private boolean validateUserConnectorInterfaces(
356       Collection<UserConnectorInfo> connectorInterfaces) {
357     boolean isValid = true;
358 
359     for (UserConnectorInfo connectorInterface : connectorInterfaces) {
360       isValid = validateUserConnectorInterface(connectorInterface) && isValid;
361     }
362 
363     return isValid;
364   }
365 
validateUserConnectorInterface(UserConnectorInfo connectorInterface)366   private boolean validateUserConnectorInterface(UserConnectorInfo connectorInterface) {
367     boolean isValid = true;
368 
369     if (!connectorInterface.connectorElement().getKind().equals(ElementKind.INTERFACE)) {
370       showError(CUSTOM_USER_CONNECTOR_MUST_BE_INTERFACE, connectorInterface.connectorElement());
371       isValid = false;
372     }
373 
374     if (!implementsInterface(connectorInterface.connectorElement(), userConnectorType)) {
375       showError(CUSTOM_USER_CONNECTOR_MUST_EXTEND_CONNECTOR, connectorInterface.connectorElement());
376       isValid = false;
377     }
378 
379     for (TypeElement parcelableWrapper : connectorInterface.parcelableWrapperClasses()) {
380       if (parcelableWrapper.getAnnotation(CustomParcelableWrapper.class) == null) {
381         showError(PARCELABLE_WRAPPER_ANNOTATION_ERROR, connectorInterface.connectorElement());
382       }
383     }
384 
385     for (TypeElement futureWrapper : connectorInterface.futureWrapperClasses()) {
386       if (futureWrapper.getAnnotation(CustomFutureWrapper.class) == null) {
387         isValid = false;
388         showError(FUTURE_WRAPPER_ANNOTATION_ERROR, connectorInterface.connectorElement());
389       }
390     }
391 
392     for (TypeElement importer : connectorInterface.importsClasses()) {
393       if (importer.getAnnotation(CustomUserConnector.class) == null) {
394         isValid = false;
395         showError(IMPORTS_NOT_USER_CONNECTOR_ERROR, connectorInterface.connectorElement());
396       }
397     }
398 
399     return isValid;
400   }
401 
validateGeneratedProfileConnectors(Collection<TypeElement> generatedConnectors)402   private boolean validateGeneratedProfileConnectors(Collection<TypeElement> generatedConnectors) {
403     boolean isValid = true;
404 
405     for (TypeElement generatedConnector : generatedConnectors) {
406       isValid = validateGeneratedProfileConnector(generatedConnector) && isValid;
407     }
408 
409     return isValid;
410   }
411 
validateGeneratedProfileConnector(TypeElement generatedConnector)412   private boolean validateGeneratedProfileConnector(TypeElement generatedConnector) {
413     boolean isValid = true;
414 
415     if (!generatedConnector.getKind().equals(ElementKind.INTERFACE)) {
416       showError(GENERATED_PROFILE_CONNECTOR_MUST_BE_INTERFACE, generatedConnector);
417       isValid = false;
418     }
419 
420     if (!implementsInterface(generatedConnector, profileConnectorType)) {
421       showError(GENERATED_PROFILE_CONNECTOR_MUST_EXTEND_PROFILE_CONNECTOR, generatedConnector);
422       isValid = false;
423     }
424 
425     if (generatedConnector.getEnclosedElements().stream()
426         .anyMatch(i -> !i.getModifiers().contains(Modifier.STATIC))) {
427       showError(ADDITIONAL_PROFILE_CONNECTOR_METHODS_ERROR, generatedConnector);
428       isValid = false;
429     }
430 
431     return isValid;
432   }
433 
validateGeneratedUserConnectors(Collection<TypeElement> generatedConnectors)434   private boolean validateGeneratedUserConnectors(Collection<TypeElement> generatedConnectors) {
435     boolean isValid = true;
436 
437     for (TypeElement generatedConnector : generatedConnectors) {
438       isValid = validateGeneratedUserConnector(generatedConnector) && isValid;
439     }
440 
441     return isValid;
442   }
443 
validateGeneratedUserConnector(TypeElement generatedConnector)444   private boolean validateGeneratedUserConnector(TypeElement generatedConnector) {
445     boolean isValid = true;
446 
447     if (!generatedConnector.getKind().equals(ElementKind.INTERFACE)) {
448       showError(GENERATED_USER_CONNECTOR_MUST_BE_INTERFACE, generatedConnector);
449       isValid = false;
450     }
451 
452     if (!implementsInterface(generatedConnector, userConnectorType)) {
453       showError(GENERATED_USER_CONNECTOR_MUST_EXTEND_USER_CONNECTOR, generatedConnector);
454       isValid = false;
455     }
456 
457     if (generatedConnector.getEnclosedElements().stream()
458         .anyMatch(i -> !i.getModifiers().contains(Modifier.STATIC))) {
459       showError(ADDITIONAL_USER_CONNECTOR_METHODS_ERROR, generatedConnector);
460       isValid = false;
461     }
462 
463     return isValid;
464   }
465 
implementsInterface(TypeElement type, TypeMirror interfaceType)466   private boolean implementsInterface(TypeElement type, TypeMirror interfaceType) {
467     for (TypeMirror t : type.getInterfaces()) {
468       if (validatorContext.types().isSameType(t, interfaceType)) {
469         return true;
470       }
471     }
472     return false;
473   }
474 
validateConfigurations( Collection<ValidatorCrossProfileConfigurationInfo> configurations)475   private boolean validateConfigurations(
476       Collection<ValidatorCrossProfileConfigurationInfo> configurations) {
477     boolean isValid = true;
478 
479     for (ValidatorCrossProfileConfigurationInfo configuration : configurations) {
480       isValid = validateConfiguration(configuration) && isValid;
481     }
482 
483     return isValid;
484   }
485 
validateConfiguration(ValidatorCrossProfileConfigurationInfo configuration)486   private boolean validateConfiguration(ValidatorCrossProfileConfigurationInfo configuration) {
487     boolean isValid = true;
488 
489     for (TypeElement providerClass : configuration.providerClassElements()) {
490       if (!hasCrossProfileProviderAnnotation(providerClass)
491           && GeneratorUtilities.findCrossProfileProviderMethodsInClass(providerClass).isEmpty()) {
492         showError(NOT_A_PROVIDER_CLASS_ERROR, configuration.configurationElement());
493         isValid = false;
494       }
495     }
496 
497     if (configuration.connector().isPresent()
498         && !configuration.connector().get().getKind().equals(ElementKind.INTERFACE)) {
499       showError(CONNECTOR_MUST_BE_INTERFACE, configuration.configurationElement());
500       isValid = false;
501     }
502 
503     if (configuration.connector().isPresent()
504         && !implementsInterface(configuration.connector().get(), profileConnectorType)
505         && !implementsInterface(configuration.connector().get(), userConnectorType)) {
506       showError(CONNECTOR_MUST_EXTEND_CONNECTOR, configuration.configurationElement());
507       isValid = false;
508     }
509 
510     return isValid;
511   }
512 
validateCrossProfileTypes( Collection<ValidatorCrossProfileTypeInfo> crossProfileTypes)513   private boolean validateCrossProfileTypes(
514       Collection<ValidatorCrossProfileTypeInfo> crossProfileTypes) {
515     boolean isValid =
516         validateCrossProfileTypesAreProvided(
517             crossProfileTypes.stream()
518                 .map(ValidatorCrossProfileTypeInfo::crossProfileTypeElement)
519                 .collect(toSet()),
520             validatorContext.newProviderMethods(),
521             validatorContext.newProviderClasses());
522 
523     for (ValidatorCrossProfileTypeInfo crossProfileType : crossProfileTypes) {
524       isValid =
525           validateCrossProfileType(crossProfileType)
526               && validateAdditionalUsedTypes(crossProfileType)
527               && isValid;
528     }
529 
530     return isValid;
531   }
532 
validateAdditionalUsedTypes(ValidatorCrossProfileTypeInfo crossProfileType)533   private boolean validateAdditionalUsedTypes(ValidatorCrossProfileTypeInfo crossProfileType) {
534     boolean isValid = true;
535     ImmutableList<TypeElement> additionalUsedTypes =
536         crossProfileType.additionalUsedTypes().asList();
537 
538     for (TypeElement supportedType : additionalUsedTypes) {
539       if (!crossProfileType
540               .supportedTypes()
541               .isValidReturnType(supportedType.asType(), /* check generics */ false)
542           && !crossProfileType
543               .supportedTypes()
544               .isValidParameterType(supportedType.asType(), /* check generics */ false)) {
545 
546         showError(
547             String.format(ADDITIONAL_TYPE_INVALID_TYPE_ERROR, supportedType.getSimpleName()),
548             supportedType);
549         isValid = false;
550       }
551     }
552 
553     return isValid;
554   }
555 
validateCrossProfileType(ValidatorCrossProfileTypeInfo crossProfileType)556   private boolean validateCrossProfileType(ValidatorCrossProfileTypeInfo crossProfileType) {
557     boolean isValid = true;
558 
559     PackageElement packageElement =
560         (PackageElement) crossProfileType.crossProfileTypeElement().getEnclosingElement();
561     if (packageElement.getQualifiedName().toString().isEmpty()) {
562       showError(
563           CROSS_PROFILE_TYPE_DEFAULT_PACKAGE_ERROR,
564           crossProfileType.crossProfileTypeElement(),
565           validationMessageFormatterForClass(crossProfileType.crossProfileTypeElement()));
566       isValid = false;
567     }
568 
569     if (!crossProfileType.crossProfileTypeElement().getModifiers().contains(Modifier.PUBLIC)) {
570       showError(
571           NON_PUBLIC_CROSS_PROFILE_TYPE_ERROR,
572           crossProfileType.crossProfileTypeElement(),
573           validationMessageFormatterForClass(crossProfileType.crossProfileTypeElement()));
574       isValid = false;
575     }
576 
577     if (crossProfileType.isStatic()) {
578       for (ExecutableElement crossProfileMethod : crossProfileType.crossProfileMethods()) {
579         if (!crossProfileMethod.getModifiers().contains(Modifier.STATIC)) {
580           showError(NOT_STATIC_ERROR, crossProfileMethod);
581           isValid = false;
582         }
583       }
584     }
585 
586     if (crossProfileType.connectorInfo().isPresent()
587         && !crossProfileType
588             .connectorInfo()
589             .get()
590             .connectorElement()
591             .getKind()
592             .equals(ElementKind.INTERFACE)) {
593       showError(CONNECTOR_MUST_BE_INTERFACE, crossProfileType.crossProfileTypeElement());
594       isValid = false;
595     }
596 
597     if (crossProfileType.connectorInfo().isPresent()
598         && !implementsInterface(
599             crossProfileType.connectorInfo().get().connectorElement(), profileConnectorType)
600         && !implementsInterface(
601             crossProfileType.connectorInfo().get().connectorElement(), userConnectorType)) {
602       showError(CONNECTOR_MUST_EXTEND_CONNECTOR, crossProfileType.crossProfileTypeElement());
603       isValid = false;
604     }
605 
606     for (TypeElement parcelableWrapper : crossProfileType.parcelableWrapperClasses()) {
607       if (parcelableWrapper.getAnnotation(CustomParcelableWrapper.class) == null) {
608         showError(PARCELABLE_WRAPPER_ANNOTATION_ERROR, crossProfileType.crossProfileTypeElement());
609       }
610     }
611 
612     for (TypeElement futureWrapper : crossProfileType.futureWrapperClasses()) {
613       if (futureWrapper.getAnnotation(CustomFutureWrapper.class) == null) {
614         isValid = false;
615         showError(FUTURE_WRAPPER_ANNOTATION_ERROR, crossProfileType.crossProfileTypeElement());
616       }
617     }
618 
619     isValid =
620         crossProfileType.crossProfileMethods().stream()
621                 .map(m -> validateCrossProfileMethod(crossProfileType, m))
622                 .allMatch(b -> b)
623             && isValid;
624 
625     return isValid;
626   }
627 
validateCrossProfileTypesAreProvided( Collection<TypeElement> crossProfileTypeElements, Collection<ExecutableElement> providerMethods, Collection<ValidatorProviderClassInfo> providerClasses)628   private boolean validateCrossProfileTypesAreProvided(
629       Collection<TypeElement> crossProfileTypeElements,
630       Collection<ExecutableElement> providerMethods,
631       Collection<ValidatorProviderClassInfo> providerClasses) {
632     Map<String, Collection<Element>> crossProfileTypeProviders =
633         crossProfileTypeElements.stream()
634             .collect(toMap(element -> element.asType().toString(), element -> new HashSet<>()));
635 
636     for (ExecutableElement provider : providerMethods) {
637       String providedTypeName = provider.getReturnType().toString();
638 
639       if (crossProfileTypeProviders.containsKey(providedTypeName)) {
640         crossProfileTypeProviders.get(providedTypeName).add(provider);
641       }
642     }
643 
644     for (ValidatorProviderClassInfo provider : providerClasses) {
645       for (TypeElement staticType : provider.staticTypes()) {
646         String providedTypeName = staticType.getQualifiedName().toString();
647 
648         if (crossProfileTypeProviders.containsKey(providedTypeName)) {
649           crossProfileTypeProviders.get(providedTypeName).add(provider.providerClassElement());
650         }
651       }
652     }
653 
654     boolean isValid = true;
655 
656     for (String crossProfileType : crossProfileTypeProviders.keySet()) {
657       Collection<Element> providers = crossProfileTypeProviders.get(crossProfileType);
658 
659       if (providers.size() > 1) {
660         isValid = false;
661         for (Element providerElement : providers) {
662           showError(String.format(MULTIPLE_PROVIDERS_ERROR, crossProfileType), providerElement);
663         }
664       }
665     }
666 
667     return isValid;
668   }
669 
validateProviderMethods(Collection<ExecutableElement> providerMethods)670   private boolean validateProviderMethods(Collection<ExecutableElement> providerMethods) {
671     boolean isValid = true;
672 
673     for (ExecutableElement providerMethod : providerMethods) {
674       TypeElement crossProfileType =
675           validatorContext.elements().getTypeElement(providerMethod.getReturnType().toString());
676       if (!hasCrossProfileAnnotation(crossProfileType)
677           && findCrossProfileMethodsInClass(crossProfileType).isEmpty()) {
678         showError(PROVIDING_NON_CROSS_PROFILE_TYPE_ERROR, providerMethod);
679         isValid = false;
680       }
681 
682       if (providerMethod.getParameters().stream()
683               .anyMatch(v -> !validatorContext.types().isSameType(v.asType(), contextType))
684           || providerMethod.getParameters().size() > 1) {
685         showError(PROVIDER_INCORRECT_ARGS_ERROR, providerMethod);
686         isValid = false;
687       }
688 
689       if (providerMethod.getModifiers().contains(Modifier.STATIC)) {
690         showError(STATIC_PROVIDER_ERROR, providerMethod);
691         isValid = false;
692       }
693 
694       CrossProfileProviderAnnotationInfo annotationInfo =
695           AnnotationFinder.extractCrossProfileProviderAnnotationInfo(
696               validatorContext, providerMethod);
697 
698       if (!annotationInfo.staticTypes().isEmpty()) {
699         showError(METHOD_STATICTYPES_ERROR, providerMethod);
700         isValid = false;
701       }
702     }
703 
704     return isValid;
705   }
706 
validateProviderClasses(Collection<ValidatorProviderClassInfo> providerClasses)707   private boolean validateProviderClasses(Collection<ValidatorProviderClassInfo> providerClasses) {
708     boolean isValid = true;
709 
710     for (ValidatorProviderClassInfo provider : providerClasses) {
711       if (!hasValidProviderClassConstructor(provider.providerClassElement())) {
712         showError(INVALID_CONSTRUCTORS_ERROR, provider.providerClassElement());
713         isValid = false;
714       }
715 
716       if (provider.providerClassElement().getEnclosedElements().stream()
717               .filter(e -> e instanceof ExecutableElement)
718               .map(e -> (ExecutableElement) e)
719               .filter(e -> e.getKind().equals(ElementKind.CONSTRUCTOR))
720               .filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
721               .count()
722           > 1) {
723         showError(INVALID_CONSTRUCTORS_ERROR, provider.providerClassElement());
724         isValid = false;
725       }
726     }
727 
728     return isValid;
729   }
730 
hasValidProviderClassConstructor(TypeElement clazz)731   private boolean hasValidProviderClassConstructor(TypeElement clazz) {
732     for (ExecutableElement constructor :
733         ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
734       if (constructor.getModifiers().contains(Modifier.PUBLIC)) {
735         if (isValidProviderClassConstructor(constructor)) {
736           return true;
737         }
738       }
739     }
740     return false;
741   }
742 
isValidProviderClassConstructor(ExecutableElement constructor)743   private boolean isValidProviderClassConstructor(ExecutableElement constructor) {
744     if (constructor.getParameters().size() == 0) {
745       return true;
746     }
747 
748     if (constructor.getParameters().size() > 1) {
749       return false;
750     }
751 
752     return validatorContext
753         .types()
754         .isSameType(constructor.getParameters().iterator().next().asType(), contextType);
755   }
756 
validateCrossProfileMethod( ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod)757   private boolean validateCrossProfileMethod(
758       ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod) {
759     boolean isValid = true;
760 
761     CrossProfileAnnotationInfo crossProfileAnnotation =
762         AnnotationFinder.extractCrossProfileAnnotationInfo(validatorContext, crossProfileMethod);
763 
764     if (!crossProfileAnnotation.connectorIsDefault()) {
765       showError(METHOD_CONNECTOR_ERROR, crossProfileMethod);
766       isValid = false;
767     }
768 
769     if (!crossProfileAnnotation.parcelableWrapperClasses().isEmpty()) {
770       showError(METHOD_PARCELABLE_WRAPPERS_ERROR, crossProfileMethod);
771       isValid = false;
772     }
773 
774     if (!crossProfileMethod.getThrownTypes().isEmpty()) {
775       if (CrossProfileMethodInfo.isFuture(crossProfileType.supportedTypes(), crossProfileMethod)
776           || CrossProfileMethodInfo.getCrossProfileCallbackParam(
777                   validatorContext, crossProfileMethod)
778               .isPresent()) {
779         showError(ASYNC_DECLARED_EXCEPTION_ERROR, crossProfileMethod);
780         isValid = false;
781       }
782     }
783 
784     if (crossProfileAnnotation.isStatic()) {
785       showError(METHOD_ISSTATIC_ERROR, crossProfileMethod);
786       isValid = false;
787     }
788 
789     isValid =
790         isValid
791             && validateReturnType(crossProfileType, crossProfileMethod)
792             && validateParameterTypesForCrossProfileMethod(crossProfileType, crossProfileMethod);
793 
794     if (crossProfileMethod.getAnnotation(Cacheable.class) != null) {
795       isValid = isValid && validateCacheableMethod(crossProfileType, crossProfileMethod);
796     }
797 
798     return isValid;
799   }
800 
validateReturnType( ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod)801   private boolean validateReturnType(
802       ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod) {
803     TypeMirror returnType = crossProfileMethod.getReturnType();
804 
805     if (crossProfileType.supportedTypes().isValidReturnType(returnType)) {
806       return true;
807     }
808 
809     showError(String.format(UNSUPPORTED_RETURN_TYPE_ERROR, returnType), crossProfileMethod);
810     return false;
811   }
812 
validateParameterTypesForCrossProfileMethod( ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod)813   private boolean validateParameterTypesForCrossProfileMethod(
814       ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement crossProfileMethod) {
815     boolean isValid =
816         crossProfileMethod.getParameters().stream()
817             .allMatch(p -> validateParameterTypeForCrossProfileMethod(crossProfileType, p));
818 
819     List<TypeElement> crossProfileCallbackParameters =
820         crossProfileMethod.getParameters().stream()
821             .map(v -> validatorContext.elements().getTypeElement(v.asType().toString()))
822             .filter(Objects::nonNull)
823             .filter(AnnotationFinder::hasCrossProfileCallbackAnnotation)
824             .collect(Collectors.toList());
825 
826     if (crossProfileCallbackParameters.size() > 1) {
827       isValid = false;
828       showError(MULTIPLE_ASYNC_CALLBACK_PARAMETERS_ERROR, crossProfileMethod);
829     }
830 
831     if (crossProfileCallbackParameters.size() == 1) {
832       if (!crossProfileMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
833         isValid = false;
834         showError(NON_VOID_CALLBACK_ERROR, crossProfileMethod);
835       }
836 
837       isValid =
838           validateParameterTypesForCrossProfileCallbackInterface(
839                   crossProfileType, crossProfileCallbackParameters.get(0))
840               && isValid;
841     }
842 
843     if (!crossProfileCallbackParameters.isEmpty()
844         && !crossProfileMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
845       isValid = false;
846       showError(NON_VOID_CALLBACK_ERROR, crossProfileMethod);
847     }
848 
849     return isValid;
850   }
851 
validateParameterTypeForCrossProfileCallbackInterface( ValidatorCrossProfileTypeInfo crossProfileType, VariableElement parameter)852   private boolean validateParameterTypeForCrossProfileCallbackInterface(
853       ValidatorCrossProfileTypeInfo crossProfileType, VariableElement parameter) {
854     TypeMirror parameterType = parameter.asType();
855 
856     if (crossProfileType
857         .supportedTypes()
858         .isValidParameterType(
859             parameterType, TypeCheckContext.createForCrossProfileCallbackInterface())) {
860       return true;
861     }
862 
863     showError(
864         String.format(UNSUPPORTED_PARAMETER_TYPE_CROSS_ASYNC_CALLBACK, parameterType),
865         parameter,
866         validationMessageFormatterFor(crossProfileType.crossProfileMethods().get(0)));
867     return false;
868   }
869 
validateParameterTypesForCrossProfileCallbackInterface( ValidatorCrossProfileTypeInfo crossProfileType, TypeElement crossProfileCallbackInterface)870   private boolean validateParameterTypesForCrossProfileCallbackInterface(
871       ValidatorCrossProfileTypeInfo crossProfileType, TypeElement crossProfileCallbackInterface) {
872     return crossProfileCallbackInterface.getEnclosedElements().stream()
873         .filter(m -> m instanceof ExecutableElement)
874         .map(m -> (ExecutableElement) m)
875         .map(m -> validateParameterTypesForCrossProfileCallbackInterface(crossProfileType, m))
876         .allMatch(b -> b);
877   }
878 
validateParameterTypesForCrossProfileCallbackInterface( ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement method)879   private boolean validateParameterTypesForCrossProfileCallbackInterface(
880       ValidatorCrossProfileTypeInfo crossProfileType, ExecutableElement method) {
881     return method.getParameters().stream()
882         .allMatch(m -> validateParameterTypeForCrossProfileCallbackInterface(crossProfileType, m));
883   }
884 
validateParameterTypeForCrossProfileMethod( ValidatorCrossProfileTypeInfo crossProfileType, VariableElement parameter)885   private boolean validateParameterTypeForCrossProfileMethod(
886       ValidatorCrossProfileTypeInfo crossProfileType, VariableElement parameter) {
887     TypeMirror parameterType = parameter.asType();
888 
889     if (crossProfileType.supportedTypes().isValidParameterType(parameterType)) {
890       return true;
891     }
892 
893     showError(
894         String.format(UNSUPPORTED_PARAMETER_TYPE_CROSS_PROFILE_METHOD, parameterType),
895         parameter,
896         validationMessageFormatterFor(crossProfileType.crossProfileMethods().get(0)));
897     return false;
898   }
899 
validateCrossProfileCallbackInterfaces( Collection<TypeElement> crossProfileCallbackInterfaces)900   private boolean validateCrossProfileCallbackInterfaces(
901       Collection<TypeElement> crossProfileCallbackInterfaces) {
902     return crossProfileCallbackInterfaces.stream()
903         .allMatch(this::validateCrossProfileCallbackInterface);
904   }
905 
validateCrossProfileCallbackInterface(TypeElement crossProfileCallbackInterface)906   private boolean validateCrossProfileCallbackInterface(TypeElement crossProfileCallbackInterface) {
907     boolean isValid = true;
908 
909     CrossProfileCallbackAnnotationInfo annotationInfo =
910         AnnotationFinder.extractCrossProfileCallbackAnnotationInfo(
911             validatorContext, crossProfileCallbackInterface);
912 
913     PackageElement packageElement =
914         (PackageElement) crossProfileCallbackInterface.getEnclosingElement();
915     if (packageElement.getQualifiedName().toString().isEmpty()) {
916       showError(CALLBACK_INTERFACE_DEFAULT_PACKAGE_ERROR, crossProfileCallbackInterface);
917       isValid = false;
918     }
919 
920     if (crossProfileCallbackInterface.getKind() != ElementKind.INTERFACE) {
921       showError(NOT_INTERFACE_ERROR, crossProfileCallbackInterface);
922       isValid = false;
923     }
924 
925     if (!crossProfileCallbackInterface.getTypeParameters().isEmpty()) {
926       showError(GENERIC_CALLBACK_INTERFACE_ERROR, crossProfileCallbackInterface);
927       isValid = false;
928     }
929 
930     Collection<ExecutableElement> methods = getMethods(crossProfileCallbackInterface);
931 
932     if (methods.isEmpty()) {
933       showError(NO_METHODS_ERROR, crossProfileCallbackInterface);
934       isValid = false;
935     }
936 
937     if (annotationInfo.simple() && methods.size() > 1) {
938       showError(NOT_ONE_METHOD_ERROR, crossProfileCallbackInterface);
939       isValid = false;
940     }
941 
942     isValid =
943         methods.stream()
944                 .allMatch(
945                     (method) ->
946                         validateMethodOnCrossProfileCallbackInterface(
947                             annotationInfo, method, crossProfileCallbackInterface))
948             && isValid;
949 
950     return isValid;
951   }
952 
validateMethodOnCrossProfileCallbackInterface( CrossProfileCallbackAnnotationInfo annotationInfo, ExecutableElement method, TypeElement crossProfileCallbackInterface)953   private boolean validateMethodOnCrossProfileCallbackInterface(
954       CrossProfileCallbackAnnotationInfo annotationInfo,
955       ExecutableElement method,
956       TypeElement crossProfileCallbackInterface) {
957     boolean isValid = true;
958 
959     if (method.isDefault()) {
960       showError(
961           DEFAULT_METHOD_ERROR,
962           method,
963           validationMessageFormatterFor(crossProfileCallbackInterface));
964       isValid = false;
965     }
966 
967     if (method.getModifiers().contains(Modifier.STATIC)) {
968       showError(
969           STATIC_METHOD_ERROR,
970           method,
971           validationMessageFormatterFor(crossProfileCallbackInterface));
972       isValid = false;
973     }
974 
975     if (!method.getReturnType().getKind().equals(TypeKind.VOID)) {
976       showError(
977           NOT_VOID_ERROR, method, validationMessageFormatterFor(crossProfileCallbackInterface));
978       isValid = false;
979     }
980 
981     if (annotationInfo.simple() && method.getParameters().size() > 1) {
982       showError(
983           MORE_THAN_ONE_PARAMETER_ERROR,
984           method,
985           validationMessageFormatterFor(crossProfileCallbackInterface));
986       isValid = false;
987     }
988 
989     return isValid;
990   }
991 
validateCacheableMethod( ValidatorCrossProfileTypeInfo crossProfileTypeInfo, ExecutableElement cacheableMethod)992   private boolean validateCacheableMethod(
993       ValidatorCrossProfileTypeInfo crossProfileTypeInfo, ExecutableElement cacheableMethod) {
994     boolean isValid = true;
995 
996     TypeMirror returnType = cacheableMethod.getReturnType();
997 
998     if (returnType.getKind().equals(TypeKind.VOID)) {
999       isValid = isValid && validateCallbackOnCacheableMethod(cacheableMethod);
1000     } else if (!isSerializable(crossProfileTypeInfo, returnType)) {
1001       showError(CACHEABLE_METHOD_RETURNS_NON_SERIALIZABLE_ERROR, cacheableMethod);
1002       isValid = false;
1003     }
1004 
1005     return isValid;
1006   }
1007 
isSerializable( ValidatorCrossProfileTypeInfo crossProfileTypeInfo, TypeMirror type)1008   private boolean isSerializable(
1009       ValidatorCrossProfileTypeInfo crossProfileTypeInfo, TypeMirror type) {
1010     return isSerializable(type) || isFutureWithSerializableResult(crossProfileTypeInfo, type);
1011   }
1012 
isSerializable(TypeMirror type)1013   private boolean isSerializable(TypeMirror type) {
1014     TypeMirror serializable =
1015         validatorContext.elements().getTypeElement(Serializable.class.getCanonicalName()).asType();
1016 
1017     return validatorContext.types().isAssignable(type, serializable);
1018   }
1019 
isFutureWithSerializableResult( ValidatorCrossProfileTypeInfo crossProfileType, TypeMirror type)1020   private boolean isFutureWithSerializableResult(
1021       ValidatorCrossProfileTypeInfo crossProfileType, TypeMirror type) {
1022 
1023     if (!crossProfileType.supportedTypes().isFuture(removeTypeArguments(type))) {
1024       return false;
1025     }
1026 
1027     TypeMirror futureResult = extractTypeArguments(type).get(0);
1028 
1029     return isSerializable(futureResult);
1030   }
1031 
validateCallbackOnCacheableMethod(ExecutableElement cacheableMethod)1032   private boolean validateCallbackOnCacheableMethod(ExecutableElement cacheableMethod) {
1033     boolean isValid = true;
1034 
1035     if (!hasCallback(cacheableMethod)) {
1036       showError(CACHEABLE_METHOD_RETURNS_VOID_ERROR, cacheableMethod);
1037       return false;
1038     }
1039 
1040     TypeElement callback =
1041         cacheableMethod.getParameters().stream()
1042             .map(v -> validatorContext.elements().getTypeElement(v.asType().toString()))
1043             .filter(Objects::nonNull)
1044             .filter(AnnotationFinder::hasCrossProfileCallbackAnnotation)
1045             .findFirst()
1046             .get();
1047 
1048     CrossProfileCallbackAnnotationInfo annotationInfo =
1049         AnnotationFinder.extractCrossProfileCallbackAnnotationInfo(validatorContext, callback);
1050     if (!annotationInfo.simple()) {
1051       showError(CACHEABLE_METHOD_NON_SIMPLE_CALLBACK_ERROR, cacheableMethod);
1052       isValid = false;
1053     }
1054 
1055     ExecutableElement method = getMethods(callback).stream().findFirst().get();
1056     if (!hasSingleSerializableParameterOnly(method)) {
1057       showError(CACHEABLE_METHOD_USES_INVALID_PARAMETERS_ERROR, cacheableMethod);
1058       isValid = false;
1059     }
1060 
1061     return isValid;
1062   }
1063 
hasCallback(ExecutableElement method)1064   private boolean hasCallback(ExecutableElement method) {
1065     return method.getParameters().stream()
1066         .map(v -> validatorContext.elements().getTypeElement(v.asType().toString()))
1067         .filter(Objects::nonNull)
1068         .anyMatch(AnnotationFinder::hasCrossProfileCallbackAnnotation);
1069   }
1070 
hasSingleSerializableParameterOnly(ExecutableElement method)1071   private boolean hasSingleSerializableParameterOnly(ExecutableElement method) {
1072     return method.getParameters().stream().filter(p -> isSerializable(p.asType())).count() == 1;
1073   }
1074 
validateCrossProfileTests( Collection<ValidatorCrossProfileTestInfo> crossProfileTests)1075   private boolean validateCrossProfileTests(
1076       Collection<ValidatorCrossProfileTestInfo> crossProfileTests) {
1077     return crossProfileTests.stream().allMatch(this::validateCrossProfileTest);
1078   }
1079 
validateCrossProfileTest(ValidatorCrossProfileTestInfo crossProfileTest)1080   private boolean validateCrossProfileTest(ValidatorCrossProfileTestInfo crossProfileTest) {
1081     boolean isValid = true;
1082 
1083     if (!hasCrossProfileConfigurationAnnotation(crossProfileTest.configurationElement())
1084         && !hasCrossProfileConfigurationsAnnotation(crossProfileTest.configurationElement())) {
1085       showError(NOT_A_CONFIGURATION_ERROR, crossProfileTest.crossProfileTestElement());
1086       isValid = false;
1087     }
1088     return isValid;
1089   }
1090 
validateCustomParcelableWrappers( Collection<TypeElement> customParcelableWrappers)1091   private boolean validateCustomParcelableWrappers(
1092       Collection<TypeElement> customParcelableWrappers) {
1093     return customParcelableWrappers.stream().allMatch(this::validateCustomParcelableWrapper);
1094   }
1095 
validateCustomParcelableWrapper(TypeElement customParcelableWrapper)1096   private boolean validateCustomParcelableWrapper(TypeElement customParcelableWrapper) {
1097     boolean isValid = true;
1098     if (!validatorContext.types().isAssignable(customParcelableWrapper.asType(), parcelableType)) {
1099       showError(NOT_PARCELABLE_ERROR, customParcelableWrapper);
1100       isValid = false;
1101     }
1102 
1103     ClassName parcelableWrapperRawType = getRawTypeClassName(customParcelableWrapper.asType());
1104     ClassName wrappedParamRawType =
1105         getRawTypeClassName(
1106             ParcelableWrapperAnnotationInfo.extractFromParcelableWrapperAnnotation(
1107                     validatorContext,
1108                     customParcelableWrapper.getAnnotation(CustomParcelableWrapper.class))
1109                 .originalType()
1110                 .asType());
1111 
1112     Optional<ExecutableElement> ofMethod =
1113         customParcelableWrapper.getEnclosedElements().stream()
1114             .filter(p -> p.getKind().equals(ElementKind.METHOD))
1115             .map(p -> (ExecutableElement) p)
1116             .filter(p -> p.getSimpleName().contentEquals("of"))
1117             // We drop generics as without being overly prescriptive it's impossible to know that
1118             // the method is returning the correct generic type
1119             .filter(
1120                 p ->
1121                     getRawTypeClassName(p.getReturnType())
1122                         .equals(getRawTypeClassName(customParcelableWrapper.asType())))
1123             .filter(p -> ofMethodHasExpectedArguments(wrappedParamRawType, p))
1124             .findFirst();
1125 
1126     if (!ofMethod.isPresent()) {
1127       showError(INCORRECT_OF_METHOD, customParcelableWrapper);
1128       isValid = false;
1129     }
1130 
1131     Optional<ExecutableElement> getMethod =
1132         customParcelableWrapper.getEnclosedElements().stream()
1133             .filter(p -> p.getKind().equals(ElementKind.METHOD))
1134             .map(p -> (ExecutableElement) p)
1135             .filter(p -> p.getSimpleName().contentEquals("get"))
1136             // We drop generics as without being overly prescriptive it's impossible to know that
1137             // the method is returning the correct generic type
1138             .filter(p -> getRawTypeClassName(p.getReturnType()).equals(wrappedParamRawType))
1139             .findFirst();
1140 
1141     if (!getMethod.isPresent()) {
1142       showError(INCORRECT_GET_METHOD, customParcelableWrapper);
1143       isValid = false;
1144     }
1145 
1146     TypeName creatorType =
1147         ParameterizedTypeName.get(PARCELABLE_CREATOR_CLASSNAME, parcelableWrapperRawType);
1148 
1149     Optional<VariableElement> creator =
1150         customParcelableWrapper.getEnclosedElements().stream()
1151             .filter(p -> p.getKind().equals(ElementKind.FIELD))
1152             .map(p -> (VariableElement) p)
1153             .filter(p -> p.getSimpleName().contentEquals("CREATOR"))
1154             .filter(
1155                 p ->
1156                     p.getModifiers()
1157                         .containsAll(
1158                             Arrays.asList(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)))
1159             .filter(p -> ClassName.get(p.asType()).equals(creatorType))
1160             .findFirst();
1161 
1162     if (!creator.isPresent()) {
1163       showError(INCORRECT_PARCELABLE_IMPLEMENTATION, customParcelableWrapper);
1164       isValid = false;
1165     }
1166 
1167     return isValid;
1168   }
1169 
ofMethodHasExpectedArguments( ClassName wrappedParamRawType, ExecutableElement ofMethod)1170   private boolean ofMethodHasExpectedArguments(
1171       ClassName wrappedParamRawType, ExecutableElement ofMethod) {
1172     List<? extends VariableElement> parameters = ofMethod.getParameters();
1173     if (parameters.size() != 3) {
1174       return false;
1175     }
1176 
1177     if (!validatorContext.types().isSameType(parameters.get(0).asType(), bundlerType)) {
1178       return false;
1179     }
1180 
1181     if (!validatorContext.types().isSameType(parameters.get(1).asType(), bundlerTypeType)) {
1182       return false;
1183     }
1184 
1185     if (!getRawTypeClassName(parameters.get(2).asType()).equals(wrappedParamRawType)) {
1186       return false;
1187     }
1188 
1189     return true;
1190   }
1191 
validateCustomFutureWrappers(Collection<TypeElement> futureWrappers)1192   private boolean validateCustomFutureWrappers(Collection<TypeElement> futureWrappers) {
1193     return futureWrappers.stream().map(this::validateCustomFutureWrapper).allMatch(b -> b);
1194   }
1195 
validateCustomFutureWrapper(TypeElement futureWrapper)1196   private boolean validateCustomFutureWrapper(TypeElement futureWrapper) {
1197     boolean isValid = true;
1198 
1199     ClassName wrappedFutureRawType =
1200         getRawTypeClassName(
1201             FutureWrapperAnnotationInfo.extractFromFutureWrapperAnnotation(
1202                     validatorContext, futureWrapper.getAnnotation(CustomFutureWrapper.class))
1203                 .originalType()
1204                 .asType());
1205 
1206     if (!getRawTypeQualifiedName(futureWrapper.getSuperclass())
1207         .equals("com.google.android.enterprise.connectedapps.FutureWrapper")) {
1208       showError(DOES_NOT_EXTEND_FUTURE_WRAPPER_ERROR, futureWrapper);
1209       isValid = false;
1210     }
1211 
1212     if (futureWrapper.getTypeParameters().size() != 1) {
1213       showError(MUST_HAVE_ONE_TYPE_PARAMETER_ERROR, futureWrapper);
1214       isValid = false;
1215     }
1216 
1217     Optional<ExecutableElement> createMethod =
1218         futureWrapper.getEnclosedElements().stream()
1219             .filter(e -> e instanceof ExecutableElement)
1220             .map(e -> (ExecutableElement) e)
1221             .filter(e -> e.getSimpleName().contentEquals("create"))
1222             .filter(
1223                 e -> e.getModifiers().containsAll(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)))
1224             // We drop generics as without being overly prescriptive it's impossible to know that
1225             // the method is returning the correct generic type
1226             .filter(
1227                 e ->
1228                     getRawTypeClassName(e.getReturnType())
1229                         .equals(getRawTypeClassName(futureWrapper.asType())))
1230             .filter(this::createMethodHasExpectedArguments)
1231             .findFirst();
1232 
1233     if (!createMethod.isPresent()) {
1234       showError(INCORRECT_CREATE_METHOD_ERROR, futureWrapper);
1235       isValid = false;
1236     }
1237 
1238     Optional<ExecutableElement> getFutureMethod =
1239         futureWrapper.getEnclosedElements().stream()
1240             .filter(e -> e instanceof ExecutableElement)
1241             .map(e -> (ExecutableElement) e)
1242             .filter(e -> e.getSimpleName().contentEquals("getFuture"))
1243             .filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
1244             .filter(e -> !e.getModifiers().contains(Modifier.STATIC))
1245             // We drop generics as without being overly prescriptive it's impossible to know that
1246             // the method is returning the correct generic type
1247             .filter(e -> getRawTypeClassName(e.getReturnType()).equals(wrappedFutureRawType))
1248             .filter(e -> e.getParameters().isEmpty())
1249             .findFirst();
1250 
1251     if (!getFutureMethod.isPresent()) {
1252       showError(INCORRECT_GET_FUTURE_METHOD_ERROR, futureWrapper);
1253       isValid = false;
1254     }
1255 
1256     Optional<ExecutableElement> writeFutureResultMethod =
1257         futureWrapper.getEnclosedElements().stream()
1258             .filter(e -> e instanceof ExecutableElement)
1259             .map(e -> (ExecutableElement) e)
1260             .filter(e -> e.getSimpleName().contentEquals("writeFutureResult"))
1261             .filter(
1262                 e -> e.getModifiers().containsAll(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)))
1263             .filter(e -> e.getReturnType().toString().equals("void"))
1264             .filter(e -> writeFutureResultMethodHasExpectedArguments(e, wrappedFutureRawType))
1265             .findFirst();
1266 
1267     if (!writeFutureResultMethod.isPresent()) {
1268       showError(INCORRECT_RESOLVE_CALLBACK_WHEN_FUTURE_IS_SET_METHOD_ERROR, futureWrapper);
1269       isValid = false;
1270     }
1271 
1272     Optional<ExecutableElement> groupResultsMethod =
1273         futureWrapper.getEnclosedElements().stream()
1274             .filter(e -> e instanceof ExecutableElement)
1275             .map(e -> (ExecutableElement) e)
1276             .filter(e -> e.getSimpleName().contentEquals("groupResults"))
1277             .filter(
1278                 e -> e.getModifiers().containsAll(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)))
1279             .filter(e -> groupResultsMethodHasExpectedReturnType(e, wrappedFutureRawType))
1280             .filter(e -> groupResultsMethodHasExpectedArguments(e, wrappedFutureRawType))
1281             .findFirst();
1282 
1283     if (!groupResultsMethod.isPresent()) {
1284       showError(INCORRECT_GROUP_RESULTS_METHOD_ERROR, futureWrapper);
1285       isValid = false;
1286     }
1287 
1288     return isValid;
1289   }
1290 
groupResultsMethodHasExpectedReturnType( ExecutableElement groupResultsMethod, ClassName wrappedFutureRawType)1291   private boolean groupResultsMethodHasExpectedReturnType(
1292       ExecutableElement groupResultsMethod, ClassName wrappedFutureRawType) {
1293 
1294     if (!getRawTypeClassName(groupResultsMethod.getReturnType()).equals(wrappedFutureRawType)) {
1295       return false;
1296     }
1297 
1298     TypeMirror wrappedReturnType = extractTypeArguments(groupResultsMethod.getReturnType()).get(0);
1299 
1300     if (!getRawTypeClassName(wrappedReturnType).equals(ClassName.get(Map.class))) {
1301       return false;
1302     }
1303 
1304     TypeMirror wrappedReturnTypeKey = extractTypeArguments(wrappedReturnType).get(0);
1305 
1306     if (!validatorContext.types().isSameType(wrappedReturnTypeKey, profileType)) {
1307       return false;
1308     }
1309 
1310     return true;
1311   }
1312 
groupResultsMethodHasExpectedArguments( ExecutableElement groupResultsMethod, ClassName wrappedFutureRawType)1313   private boolean groupResultsMethodHasExpectedArguments(
1314       ExecutableElement groupResultsMethod, ClassName wrappedFutureRawType) {
1315     if (groupResultsMethod.getParameters().size() != 1) {
1316       return false;
1317     }
1318 
1319     TypeMirror param = groupResultsMethod.getParameters().get(0).asType();
1320 
1321     if (!getRawTypeClassName(param).equals(ClassName.get(Map.class))) {
1322       return false;
1323     }
1324 
1325     List<TypeMirror> params = extractTypeArguments(param);
1326 
1327     TypeMirror keyParam = params.get(0);
1328     TypeMirror valueParam = params.get(1);
1329 
1330     if (!validatorContext.types().isSameType(keyParam, profileType)) {
1331       return false;
1332     }
1333 
1334     if (!getRawTypeClassName(valueParam).equals(wrappedFutureRawType)) {
1335       return false;
1336     }
1337 
1338     return true;
1339   }
1340 
createMethodHasExpectedArguments(ExecutableElement createMethod)1341   private boolean createMethodHasExpectedArguments(ExecutableElement createMethod) {
1342     if (createMethod.getParameters().size() != 2) {
1343       return false;
1344     }
1345 
1346     if (!validatorContext
1347         .types()
1348         .isSameType(createMethod.getParameters().get(0).asType(), bundlerType)) {
1349       return false;
1350     }
1351 
1352     if (!validatorContext
1353         .types()
1354         .isSameType(createMethod.getParameters().get(1).asType(), bundlerTypeType)) {
1355       return false;
1356     }
1357 
1358     return true;
1359   }
1360 
writeFutureResultMethodHasExpectedArguments( ExecutableElement method, ClassName wrappedFutureRawType)1361   private boolean writeFutureResultMethodHasExpectedArguments(
1362       ExecutableElement method, ClassName wrappedFutureRawType) {
1363     if (method.getParameters().size() != 2) {
1364       return false;
1365     }
1366 
1367     if (!getRawTypeClassName(method.getParameters().get(0).asType()).equals(wrappedFutureRawType)) {
1368       return false;
1369     }
1370 
1371     if (!validatorContext
1372         .types()
1373         .isAssignable(
1374             removeTypeArguments(method.getParameters().get(1).asType()), futureResultWriterType)) {
1375       return false;
1376     }
1377 
1378     return true;
1379   }
1380 
getMethods(TypeElement typeElement)1381   private Collection<ExecutableElement> getMethods(TypeElement typeElement) {
1382     return typeElement.getEnclosedElements().stream()
1383         .filter(e -> e instanceof ExecutableElement)
1384         .map(e -> (ExecutableElement) e)
1385         .collect(toSet());
1386   }
1387 
showError( String errorText, Element errorElement, ValidationMessageFormatter validationMessageFormatter)1388   private void showError(
1389       String errorText,
1390       Element errorElement,
1391       ValidationMessageFormatter validationMessageFormatter) {
1392     showErrorPreformatted(validationMessageFormatter.format(errorText), errorElement);
1393   }
1394 
showError(String errorText, Element errorElement)1395   private void showError(String errorText, Element errorElement) {
1396     showErrorPreformatted(
1397         validationMessageFormatterFor(errorElement).format(errorText), errorElement);
1398   }
1399 
showErrorPreformatted(String errorText, Element errorElement)1400   private void showErrorPreformatted(String errorText, Element errorElement) {
1401     validatorContext
1402         .processingEnv()
1403         .getMessager()
1404         .printMessage(Kind.ERROR, errorText, errorElement);
1405   }
1406 }
1407