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.annotationdiscovery; 17 18 import static java.util.stream.Collectors.toSet; 19 import static javax.lang.model.element.ElementKind.METHOD; 20 21 import com.google.android.enterprise.connectedapps.annotations.CrossProfile; 22 import com.google.android.enterprise.connectedapps.annotations.CrossProfileCallback; 23 import com.google.android.enterprise.connectedapps.annotations.CrossProfileConfiguration; 24 import com.google.android.enterprise.connectedapps.annotations.CrossProfileConfigurations; 25 import com.google.android.enterprise.connectedapps.annotations.CrossProfileProvider; 26 import com.google.android.enterprise.connectedapps.annotations.CrossUser; 27 import com.google.android.enterprise.connectedapps.annotations.CrossUserCallback; 28 import com.google.android.enterprise.connectedapps.annotations.CrossUserConfiguration; 29 import com.google.android.enterprise.connectedapps.annotations.CrossUserConfigurations; 30 import com.google.android.enterprise.connectedapps.annotations.CrossUserProvider; 31 import com.google.android.enterprise.connectedapps.processor.ValidationMessageFormatter; 32 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileAnnotationInfo; 33 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileCallbackAnnotationInfo; 34 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileConfigurationAnnotationInfo; 35 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileConfigurationsAnnotationInfo; 36 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileProviderAnnotationInfo; 37 import com.google.android.enterprise.connectedapps.processor.containers.CrossProfileTestAnnotationInfo; 38 import com.google.android.enterprise.connectedapps.testing.annotations.CrossProfileTest; 39 import com.google.android.enterprise.connectedapps.testing.annotations.CrossUserTest; 40 import com.google.common.collect.ImmutableList; 41 import java.lang.annotation.Annotation; 42 import java.util.Set; 43 import java.util.function.Function; 44 import java.util.stream.Stream; 45 import javax.annotation.processing.RoundEnvironment; 46 import javax.lang.model.element.Element; 47 import javax.lang.model.element.ExecutableElement; 48 import javax.lang.model.element.TypeElement; 49 import javax.lang.model.util.Elements; 50 import javax.lang.model.util.Types; 51 52 /** Helper methods to discover all cross-profile annotations of a specific type on elements. */ 53 public final class AnnotationFinder { 54 55 private static final AnnotationStrings CROSS_PROFILE_ANNOTATION_STRINGS = 56 AnnotationStrings.builder() 57 .setCrossProfileAnnotationClass(CrossProfile.class) 58 .setCrossProfileCallbackAnnotationClass(CrossProfileCallback.class) 59 .setCrossProfileConfigurationAnnotationClass(CrossProfileConfiguration.class) 60 .setCrossProfileConfigurationsAnnotationClass(CrossProfileConfigurations.class) 61 .setCrossProfileProviderAnnotationClass(CrossProfileProvider.class) 62 .setCrossProfileTestAnnotationClass(CrossProfileTest.class) 63 .build(); 64 65 private static final AnnotationStrings CROSS_USER_ANNOTATION_STRINGS = 66 AnnotationStrings.builder() 67 .setCrossProfileAnnotationClass(CrossUser.class) 68 .setCrossProfileCallbackAnnotationClass(CrossUserCallback.class) 69 .setCrossProfileProviderAnnotationClass(CrossUserProvider.class) 70 .setCrossProfileConfigurationAnnotationClass(CrossUserConfiguration.class) 71 .setCrossProfileConfigurationsAnnotationClass(CrossUserConfigurations.class) 72 .setCrossProfileTestAnnotationClass(CrossUserTest.class) 73 .build(); 74 75 private static final ImmutableList<AnnotationStrings> SUPPORTED_ANNOTATIONS = 76 ImmutableList.of(CROSS_PROFILE_ANNOTATION_STRINGS, CROSS_USER_ANNOTATION_STRINGS); 77 78 private static final AnnotationStrings DEFAULT_ANNOTATIONS = CROSS_PROFILE_ANNOTATION_STRINGS; 79 80 private static final Set<Class<? extends Annotation>> crossProfileAnnotations = 81 annotationsOfType(AnnotationClasses::crossProfileAnnotationClass); 82 83 private static final Set<Class<? extends Annotation>> crossProfileCallbackAnnotations = 84 annotationsOfType(AnnotationClasses::crossProfileCallbackAnnotationClass); 85 86 private static final Set<Class<? extends Annotation>> crossProfileConfigurationAnnotations = 87 annotationsOfType(AnnotationClasses::crossProfileConfigurationAnnotationClass); 88 89 private static final Set<Class<? extends Annotation>> crossProfileConfigurationsAnnotations = 90 annotationsOfType(AnnotationClasses::crossProfileConfigurationsAnnotationClass); 91 92 private static final Set<Class<? extends Annotation>> crossProfileProviderAnnotations = 93 annotationsOfType(AnnotationClasses::crossProfileProviderAnnotationClass); 94 95 private static final Set<Class<? extends Annotation>> crossProfileTestAnnotations = 96 annotationsOfType(AnnotationClasses::crossProfileTestAnnotationClass); 97 annotationStrings()98 public static Iterable<AnnotationStrings> annotationStrings() { 99 return SUPPORTED_ANNOTATIONS; 100 } 101 crossProfileAnnotationNames()102 public static AnnotationNames crossProfileAnnotationNames() { 103 return CROSS_PROFILE_ANNOTATION_STRINGS; 104 } 105 crossUserAnnotationNames()106 public static AnnotationNames crossUserAnnotationNames() { 107 return CROSS_USER_ANNOTATION_STRINGS; 108 } 109 validationMessageFormatterFor(Element element)110 public static ValidationMessageFormatter validationMessageFormatterFor(Element element) { 111 return ValidationMessageFormatter.forAnnotations(annotationNamesFor(element)); 112 } 113 annotationNamesFor(Element element)114 private static AnnotationNames annotationNamesFor(Element element) { 115 for (AnnotationStrings annotationStrings : SUPPORTED_ANNOTATIONS) { 116 if (hasAnyAnnotationsOfClass(element, annotationStrings)) { 117 return annotationStrings; 118 } 119 } 120 121 return DEFAULT_ANNOTATIONS; 122 } 123 validationMessageFormatterForClass( TypeElement typeElement)124 public static ValidationMessageFormatter validationMessageFormatterForClass( 125 TypeElement typeElement) { 126 return ValidationMessageFormatter.forAnnotations(annotationNamesForClass(typeElement)); 127 } 128 annotationNamesForClass(TypeElement typeElement)129 public static AnnotationNames annotationNamesForClass(TypeElement typeElement) { 130 for (AnnotationStrings annotationStrings : SUPPORTED_ANNOTATIONS) { 131 if (hasAnyAnnotationsOfClass(typeElement, annotationStrings)) { 132 return annotationStrings; 133 } 134 135 for (ExecutableElement method : 136 typeElement.getEnclosedElements().stream() 137 .filter(element -> element.getKind() == METHOD) 138 .map(element -> (ExecutableElement) element) 139 .collect(toSet())) { 140 if (hasAnyAnnotationsOfClass(method, annotationStrings)) { 141 return annotationStrings; 142 } 143 } 144 } 145 146 return DEFAULT_ANNOTATIONS; 147 } 148 hasAnyAnnotationsOfClass( Element element, AnnotationClasses annotationClasses)149 private static boolean hasAnyAnnotationsOfClass( 150 Element element, AnnotationClasses annotationClasses) { 151 return hasAnnotationOfClass(element, annotationClasses.crossProfileAnnotationClass()) 152 || hasAnnotationOfClass(element, annotationClasses.crossProfileCallbackAnnotationClass()) 153 || hasAnnotationOfClass(element, annotationClasses.crossProfileProviderAnnotationClass()) 154 || hasAnnotationOfClass( 155 element, annotationClasses.crossProfileConfigurationAnnotationClass()) 156 || hasAnnotationOfClass( 157 element, annotationClasses.crossProfileConfigurationsAnnotationClass()) 158 || hasAnnotationOfClass(element, annotationClasses.crossProfileTestAnnotationClass()); 159 } 160 hasAnnotationOfClass( Element element, Class<? extends Annotation> annotationClass)161 private static boolean hasAnnotationOfClass( 162 Element element, Class<? extends Annotation> annotationClass) { 163 return element.getAnnotation(annotationClass) != null; 164 } 165 extractCrossProfileAnnotationInfo( Element annotatedElement, Types types, Elements elements)166 public static CrossProfileAnnotationInfo extractCrossProfileAnnotationInfo( 167 Element annotatedElement, Types types, Elements elements) { 168 return new CrossProfileAnnotationInfoExtractor() 169 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 170 } 171 extractCrossProfileCallbackAnnotationInfo( Element annotatedElement, Types types, Elements elements)172 public static CrossProfileCallbackAnnotationInfo extractCrossProfileCallbackAnnotationInfo( 173 Element annotatedElement, Types types, Elements elements) { 174 return new CrossProfileCallbackAnnotationInfoExtractor() 175 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 176 } 177 178 public static CrossProfileConfigurationAnnotationInfo extractCrossProfileConfigurationAnnotationInfo( Element annotatedElement, Types types, Elements elements)179 extractCrossProfileConfigurationAnnotationInfo( 180 Element annotatedElement, Types types, Elements elements) { 181 return new CrossProfileConfigurationAnnotationInfoExtractor() 182 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 183 } 184 185 public static CrossProfileConfigurationsAnnotationInfo extractCrossProfileConfigurationsAnnotationInfo( Element annotatedElement, Types types, Elements elements)186 extractCrossProfileConfigurationsAnnotationInfo( 187 Element annotatedElement, Types types, Elements elements) { 188 return new CrossProfileConfigurationsAnnotationInfoExtractor() 189 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 190 } 191 extractCrossProfileProviderAnnotationInfo( Element annotatedElement, Types types, Elements elements)192 public static CrossProfileProviderAnnotationInfo extractCrossProfileProviderAnnotationInfo( 193 Element annotatedElement, Types types, Elements elements) { 194 return new CrossProfileProviderAnnotationInfoExtractor() 195 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 196 } 197 extractCrossProfileTestAnnotationInfo( Element annotatedElement, Types types, Elements elements)198 public static CrossProfileTestAnnotationInfo extractCrossProfileTestAnnotationInfo( 199 Element annotatedElement, Types types, Elements elements) { 200 return new CrossProfileTestAnnotationInfoExtractor() 201 .extractAnnotationInfo(SUPPORTED_ANNOTATIONS, annotatedElement, types, elements); 202 } 203 hasCrossProfileAnnotation(Element element)204 public static boolean hasCrossProfileAnnotation(Element element) { 205 return hasAnyAnnotations(element, crossProfileAnnotations); 206 } 207 hasCrossProfileCallbackAnnotation(Element element)208 public static boolean hasCrossProfileCallbackAnnotation(Element element) { 209 return hasAnyAnnotations(element, crossProfileCallbackAnnotations); 210 } 211 hasCrossProfileConfigurationAnnotation(Element element)212 public static boolean hasCrossProfileConfigurationAnnotation(Element element) { 213 return hasAnyAnnotations(element, crossProfileConfigurationAnnotations); 214 } 215 hasCrossProfileConfigurationsAnnotation(Element element)216 public static boolean hasCrossProfileConfigurationsAnnotation(Element element) { 217 return hasAnyAnnotations(element, crossProfileConfigurationsAnnotations); 218 } 219 hasCrossProfileProviderAnnotation(Element element)220 public static boolean hasCrossProfileProviderAnnotation(Element element) { 221 return hasAnyAnnotations(element, crossProfileProviderAnnotations); 222 } 223 hasAnyAnnotations( Element element, Set<Class<? extends Annotation>> annotations)224 private static boolean hasAnyAnnotations( 225 Element element, Set<Class<? extends Annotation>> annotations) { 226 return annotations.stream().anyMatch(annotation -> element.getAnnotation(annotation) != null); 227 } 228 elementsAnnotatedWithCrossProfile( RoundEnvironment roundEnv)229 public static Stream<? extends Element> elementsAnnotatedWithCrossProfile( 230 RoundEnvironment roundEnv) { 231 return findElementsContainingAnnotations(roundEnv, crossProfileAnnotations); 232 } 233 elementsAnnotatedWithCrossProfileCallback( RoundEnvironment roundEnv)234 public static Stream<? extends Element> elementsAnnotatedWithCrossProfileCallback( 235 RoundEnvironment roundEnv) { 236 return findElementsContainingAnnotations(roundEnv, crossProfileCallbackAnnotations); 237 } 238 elementsAnnotatedWithCrossProfileConfiguration( RoundEnvironment roundEnv)239 public static Stream<? extends Element> elementsAnnotatedWithCrossProfileConfiguration( 240 RoundEnvironment roundEnv) { 241 return findElementsContainingAnnotations(roundEnv, crossProfileConfigurationAnnotations); 242 } 243 elementsAnnotatedWithCrossProfileConfigurations( RoundEnvironment roundEnv)244 public static Stream<? extends Element> elementsAnnotatedWithCrossProfileConfigurations( 245 RoundEnvironment roundEnv) { 246 return findElementsContainingAnnotations(roundEnv, crossProfileConfigurationsAnnotations); 247 } 248 elementsAnnotatedWithCrossProfileProvider( RoundEnvironment roundEnv)249 public static Stream<? extends Element> elementsAnnotatedWithCrossProfileProvider( 250 RoundEnvironment roundEnv) { 251 return findElementsContainingAnnotations(roundEnv, crossProfileProviderAnnotations); 252 } 253 elementsAnnotatedWithCrossProfileTest( RoundEnvironment roundEnv)254 public static Stream<? extends Element> elementsAnnotatedWithCrossProfileTest( 255 RoundEnvironment roundEnv) { 256 return findElementsContainingAnnotations(roundEnv, crossProfileTestAnnotations); 257 } 258 findElementsContainingAnnotations( RoundEnvironment roundEnv, Set<Class<? extends Annotation>> annotations)259 private static Stream<? extends Element> findElementsContainingAnnotations( 260 RoundEnvironment roundEnv, Set<Class<? extends Annotation>> annotations) { 261 return annotations.stream() 262 .flatMap(annotation -> roundEnv.getElementsAnnotatedWith(annotation).stream()); 263 } 264 annotationsOfType( Function<AnnotationClasses, Class<? extends Annotation>> annotationClassGetter)265 private static Set<Class<? extends Annotation>> annotationsOfType( 266 Function<AnnotationClasses, Class<? extends Annotation>> annotationClassGetter) { 267 return SUPPORTED_ANNOTATIONS.stream().map(annotationClassGetter).collect(toSet()); 268 } 269 AnnotationFinder()270 private AnnotationFinder() {} 271 } 272