1// Copyright 2017 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14package com.google.devtools.common.options.processor; 15 16import java.lang.annotation.Annotation; 17import java.util.Map; 18import javax.lang.model.element.AnnotationMirror; 19import javax.lang.model.element.AnnotationValue; 20import javax.lang.model.element.Element; 21import javax.lang.model.element.ExecutableElement; 22import javax.lang.model.element.TypeElement; 23import javax.lang.model.type.DeclaredType; 24import javax.lang.model.type.TypeMirror; 25import javax.lang.model.util.Elements; 26import javax.lang.model.util.Types; 27 28/** Convenient utilities for dealing with the javax.lang.model types. */ 29public class ProcessorUtils { 30 31 /** Return the AnnotationMirror for the annotation of the given type on the element provided. */ 32 static AnnotationMirror getAnnotation( 33 Elements elementUtils, 34 Types typeUtils, 35 Element element, 36 Class<? extends Annotation> annotation) 37 throws OptionProcessorException { 38 TypeElement annotationElement = elementUtils.getTypeElement(annotation.getCanonicalName()); 39 if (annotationElement == null) { 40 // This can happen if the annotation is on the -processorpath but not on the -classpath. 41 throw new OptionProcessorException( 42 element, "Unable to find the type of annotation %s.", annotation); 43 } 44 TypeMirror annotationMirror = annotationElement.asType(); 45 46 for (AnnotationMirror annot : element.getAnnotationMirrors()) { 47 if (typeUtils.isSameType(annot.getAnnotationType(), annotationMirror)) { 48 return annot; 49 } 50 } 51 // No annotation of this requested type found. 52 throw new OptionProcessorException( 53 element, "No annotation %s found for this element.", annotation); 54 } 55 56 /** 57 * Returns the contents of a {@code Class}-typed field in an annotation. 58 * 59 * <p>Taken & adapted from AutoValueProcessor.java 60 * 61 * <p>This method is needed because directly reading the value of such a field from an 62 * AnnotationMirror throws: 63 * 64 * <pre> 65 * javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror Foo. 66 * </pre> 67 * 68 * @param annotation The annotation to read from. 69 * @param fieldName The name of the field to read, e.g. "exclude". 70 * @return a set of fully-qualified names of classes appearing in 'fieldName' on 'annotation' on 71 * 'element'. 72 */ 73 static TypeElement getClassTypeFromAnnotationField( 74 Elements elementUtils, AnnotationMirror annotation, String fieldName) 75 throws OptionProcessorException { 76 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : 77 elementUtils.getElementValuesWithDefaults(annotation).entrySet()) { 78 if (entry.getKey().getSimpleName().contentEquals(fieldName)) { 79 Object annotationField = entry.getValue().getValue(); 80 if (!(annotationField instanceof DeclaredType)) { 81 throw new IllegalStateException( 82 String.format( 83 "The fieldName provided should only apply to Class<> type annotation fields, " 84 + "but the field's value (%s) couldn't get cast to a DeclaredType", 85 entry)); 86 } 87 String qualifiedName = 88 ((TypeElement) ((DeclaredType) annotationField).asElement()) 89 .getQualifiedName() 90 .toString(); 91 return elementUtils.getTypeElement(qualifiedName); 92 } 93 } 94 // Annotation missing the requested field. 95 throw new OptionProcessorException( 96 null, "No member %s of the %s annotation found for element.", fieldName, annotation); 97 } 98} 99