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 com.google.android.enterprise.connectedapps.annotations.CrossProfileProvider;
19 import com.google.android.enterprise.connectedapps.annotations.CrossUserProvider;
20 import com.google.android.enterprise.connectedapps.processor.annotationdiscovery.interfaces.CrossProfileProviderAnnotation;
21 import com.google.android.enterprise.connectedapps.processor.containers.Context;
22 import java.lang.annotation.Annotation;
23 import java.lang.reflect.Proxy;
24 import javax.lang.model.element.Element;
25 import javax.lang.model.util.Elements;
26 import javax.lang.model.util.Types;
27 
28 /**
29  * An extractor which generates {@link AnnotationInfoT} for elements annotated with annotations that
30  * conform to {@link AnnotationInterfaceT}.
31  */
32 abstract class AnnotationInfoExtractor<AnnotationInfoT, AnnotationInterfaceT> {
33 
34   private final Class<AnnotationInterfaceT> annotationInterfaceClass;
35 
AnnotationInfoExtractor(Class<AnnotationInterfaceT> annotationInterfaceClass)36   AnnotationInfoExtractor(Class<AnnotationInterfaceT> annotationInterfaceClass) {
37     this.annotationInterfaceClass = annotationInterfaceClass;
38   }
39 
40   /**
41    * Returns the {@link AnnotationInfoT} that can be extracted from the first supported annotation
42    * on {@code annotatedElement}, or a default instance otherwise.
43    */
extractAnnotationInfo( Iterable<? extends AnnotationClasses> availableAnnotations, Context context, Element annotatedElement)44   AnnotationInfoT extractAnnotationInfo(
45       Iterable<? extends AnnotationClasses> availableAnnotations,
46       Context context,
47       Element annotatedElement) {
48     for (AnnotationClasses annotationClasses : availableAnnotations) {
49       Annotation annotation =
50           annotatedElement.getAnnotation(supportedAnnotationClass(annotationClasses));
51 
52       if (annotation != null) {
53         return annotationInfoFromAnnotation(
54             wrapAnnotationWithInterface(annotationInterfaceClass, annotation), context.types());
55       }
56     }
57 
58     return emptyAnnotationInfo(context.elements());
59   }
60 
61   /**
62    * Returns the class of the annotation type that this extractor generates {@link AnnotationInfoT}
63    * for.
64    *
65    * <p>For example, if supporting {@link CrossProfileProvider} and {@link CrossUserProvider}
66    * annotations, return the value of {@link
67    * AnnotationClasses#crossProfileProviderAnnotationClass()}.
68    */
supportedAnnotationClass( AnnotationClasses annotationClasses)69   protected abstract Class<? extends Annotation> supportedAnnotationClass(
70       AnnotationClasses annotationClasses);
71 
annotationInfoFromAnnotation( AnnotationInterfaceT annotation, Types types)72   protected abstract AnnotationInfoT annotationInfoFromAnnotation(
73       AnnotationInterfaceT annotation, Types types);
74 
emptyAnnotationInfo(Elements elements)75   protected abstract AnnotationInfoT emptyAnnotationInfo(Elements elements);
76 
77   /**
78    * Wraps any annotation of a specific type (e.g. {@link CrossProfileProvider} and {@link
79    * CrossUserProvider}) with its interface (in that case {@link CrossProfileProviderAnnotation}).
80    *
81    * <p>Java does not allow annotation subclassing so we use Java proxies to treat these different
82    * annotations with identical interfaces polymorphically.
83    */
wrapAnnotationWithInterface( Class<T> annotationInterfaceClass, Annotation annotation)84   protected static <T> T wrapAnnotationWithInterface(
85       Class<T> annotationInterfaceClass, Annotation annotation) {
86     return annotationInterfaceClass.cast(
87         Proxy.newProxyInstance(
88             annotationInterfaceClass.getClassLoader(),
89             new Class<?>[] {annotationInterfaceClass},
90             new AnnotationInvocationHandler(annotation)));
91   }
92 }
93