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