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 java.lang.annotation.Annotation; 22 import java.lang.reflect.Proxy; 23 import javax.lang.model.element.Element; 24 import javax.lang.model.util.Elements; 25 import javax.lang.model.util.Types; 26 27 /** 28 * An extractor which generates {@link AnnotationInfoT} for elements annotated with annotations that 29 * conform to {@link AnnotationInterfaceT}. 30 */ 31 abstract class AnnotationInfoExtractor<AnnotationInfoT, AnnotationInterfaceT> { 32 33 private final Class<AnnotationInterfaceT> annotationInterfaceClass; 34 AnnotationInfoExtractor(Class<AnnotationInterfaceT> annotationInterfaceClass)35 AnnotationInfoExtractor(Class<AnnotationInterfaceT> annotationInterfaceClass) { 36 this.annotationInterfaceClass = annotationInterfaceClass; 37 } 38 39 /** 40 * Returns the {@link AnnotationInfoT} that can be extracted from the first supported annotation 41 * on {@code annotatedElement}, or a default instance otherwise. 42 */ extractAnnotationInfo( Iterable<? extends AnnotationClasses> availableAnnotations, Element annotatedElement, Types types, Elements elements)43 AnnotationInfoT extractAnnotationInfo( 44 Iterable<? extends AnnotationClasses> availableAnnotations, 45 Element annotatedElement, 46 Types types, 47 Elements elements) { 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), types); 55 } 56 } 57 58 return emptyAnnotationInfo(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