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