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