• 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.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