1 /* 2 * Copyright (C) 2019 The Dagger Authors. 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 * http://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 17 package dagger.hilt.processor.internal; 18 19 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; 20 import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults; 21 import static com.google.auto.common.MoreTypes.asTypeElement; 22 import static com.google.common.base.Preconditions.checkNotNull; 23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 24 25 import com.google.auto.common.MoreTypes; 26 import com.google.common.collect.ImmutableList; 27 import com.google.common.collect.ImmutableSet; 28 import java.util.List; 29 import java.util.Optional; 30 import javax.lang.model.element.AnnotationMirror; 31 import javax.lang.model.element.AnnotationValue; 32 import javax.lang.model.element.AnnotationValueVisitor; 33 import javax.lang.model.element.TypeElement; 34 import javax.lang.model.element.VariableElement; 35 import javax.lang.model.type.DeclaredType; 36 import javax.lang.model.type.TypeMirror; 37 import javax.lang.model.util.SimpleAnnotationValueVisitor8; 38 39 /** A utility class for working with {@link AnnotationValue} instances. */ 40 // TODO(bcorso): Update auto-common maven import so we can use it rather than this copy. 41 public final class AnnotationValues { 42 AnnotationValues()43 private AnnotationValues() {} 44 45 private static class DefaultVisitor<T> extends SimpleAnnotationValueVisitor8<T, Void> { 46 final Class<T> clazz; 47 DefaultVisitor(Class<T> clazz)48 DefaultVisitor(Class<T> clazz) { 49 this.clazz = checkNotNull(clazz); 50 } 51 52 @Override defaultAction(Object o, Void unused)53 public T defaultAction(Object o, Void unused) { 54 throw new IllegalArgumentException( 55 "Expected a " + clazz.getSimpleName() + ", got instead: " + o); 56 } 57 } 58 59 private static final class TypeMirrorVisitor extends DefaultVisitor<DeclaredType> { 60 static final TypeMirrorVisitor INSTANCE = new TypeMirrorVisitor(); 61 TypeMirrorVisitor()62 TypeMirrorVisitor() { 63 super(DeclaredType.class); 64 } 65 66 @Override visitType(TypeMirror value, Void unused)67 public DeclaredType visitType(TypeMirror value, Void unused) { 68 return MoreTypes.asDeclared(value); 69 } 70 } 71 72 /** 73 * Returns the value as a class. 74 * 75 * @throws IllegalArgumentException if the value is not a class. 76 */ getTypeMirror(AnnotationValue value)77 public static DeclaredType getTypeMirror(AnnotationValue value) { 78 return TypeMirrorVisitor.INSTANCE.visit(value); 79 } 80 81 private static final class EnumVisitor extends DefaultVisitor<VariableElement> { 82 static final EnumVisitor INSTANCE = new EnumVisitor(); 83 EnumVisitor()84 EnumVisitor() { 85 super(VariableElement.class); 86 } 87 88 @Override visitEnumConstant(VariableElement value, Void unused)89 public VariableElement visitEnumConstant(VariableElement value, Void unused) { 90 return value; 91 } 92 } 93 94 /** Returns a class array value as a set of {@link TypeElement}. */ getTypeElements(AnnotationValue value)95 public static ImmutableSet<TypeElement> getTypeElements(AnnotationValue value) { 96 return getAnnotationValues(value).stream() 97 .map(AnnotationValues::getTypeElement) 98 .collect(toImmutableSet()); 99 } 100 101 /** Returns a class value as a {@link TypeElement}. */ getTypeElement(AnnotationValue value)102 public static TypeElement getTypeElement(AnnotationValue value) { 103 return asTypeElement(getTypeMirror(value)); 104 } 105 106 /** 107 * Returns the value as a VariableElement. 108 * 109 * @throws IllegalArgumentException if the value is not an enum. 110 */ getEnum(AnnotationValue value)111 public static VariableElement getEnum(AnnotationValue value) { 112 return EnumVisitor.INSTANCE.visit(value); 113 } 114 115 /** Returns a string array value as a set of strings. */ getStrings(AnnotationValue value)116 public static ImmutableSet<String> getStrings(AnnotationValue value) { 117 return getAnnotationValues(value).stream() 118 .map(AnnotationValues::getString) 119 .collect(toImmutableSet()); 120 } 121 122 /** 123 * Returns the value as a string. 124 * 125 * @throws IllegalArgumentException if the value is not a string. 126 */ getString(AnnotationValue value)127 public static String getString(AnnotationValue value) { 128 return valueOfType(value, String.class); 129 } 130 131 /** 132 * Returns the value as a boolean. 133 * 134 * @throws IllegalArgumentException if the value is not a boolean. 135 */ getBoolean(AnnotationValue value)136 public static boolean getBoolean(AnnotationValue value) { 137 return valueOfType(value, Boolean.class); 138 } 139 valueOfType(AnnotationValue annotationValue, Class<T> type)140 private static <T> T valueOfType(AnnotationValue annotationValue, Class<T> type) { 141 Object value = annotationValue.getValue(); 142 if (!type.isInstance(value)) { 143 throw new IllegalArgumentException( 144 "Expected " + type.getSimpleName() + ", got instead: " + value); 145 } 146 return type.cast(value); 147 } 148 149 /** Returns the int value of an annotation */ getIntValue(AnnotationMirror annotation, String valueName)150 public static int getIntValue(AnnotationMirror annotation, String valueName) { 151 return (int) getAnnotationValue(annotation, valueName).getValue(); 152 } 153 154 /** Returns an optional int value of an annotation if the value name is present */ getOptionalIntValue( AnnotationMirror annotation, String valueName)155 public static Optional<Integer> getOptionalIntValue( 156 AnnotationMirror annotation, String valueName) { 157 return isValuePresent(annotation, valueName) 158 ? Optional.of(getIntValue(annotation, valueName)) 159 : Optional.empty(); 160 } 161 162 /** Returns the String value of an annotation */ getStringValue(AnnotationMirror annotation, String valueName)163 public static String getStringValue(AnnotationMirror annotation, String valueName) { 164 return (String) getAnnotationValue(annotation, valueName).getValue(); 165 } 166 167 /** Returns an optional String value of an annotation if the value name is present */ getOptionalStringValue( AnnotationMirror annotation, String valueName)168 public static Optional<String> getOptionalStringValue( 169 AnnotationMirror annotation, String valueName) { 170 return isValuePresent(annotation, valueName) 171 ? Optional.of(getStringValue(annotation, valueName)) 172 : Optional.empty(); 173 } 174 175 /** Returns the int array value of an annotation */ getIntArrayValue(AnnotationMirror annotation, String valueName)176 public static int[] getIntArrayValue(AnnotationMirror annotation, String valueName) { 177 return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream() 178 .mapToInt(it -> (int) it.getValue()) 179 .toArray(); 180 } 181 182 /** Returns the String array value of an annotation */ getStringArrayValue(AnnotationMirror annotation, String valueName)183 public static String[] getStringArrayValue(AnnotationMirror annotation, String valueName) { 184 return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream() 185 .map(it -> (String) it.getValue()) 186 .toArray(String[]::new); 187 } 188 isValuePresent(AnnotationMirror annotation, String valueName)189 private static boolean isValuePresent(AnnotationMirror annotation, String valueName) { 190 return getAnnotationValuesWithDefaults(annotation).keySet().stream() 191 .anyMatch(member -> member.getSimpleName().contentEquals(valueName)); 192 } 193 194 /** 195 * Returns the list of values represented by an array annotation value. 196 * 197 * @throws IllegalArgumentException unless {@code annotationValue} represents an array 198 */ getAnnotationValues( AnnotationValue annotationValue)199 public static ImmutableList<AnnotationValue> getAnnotationValues( 200 AnnotationValue annotationValue) { 201 return annotationValue.accept(AS_ANNOTATION_VALUES, null); 202 } 203 204 private static final AnnotationValueVisitor<ImmutableList<AnnotationValue>, String> 205 AS_ANNOTATION_VALUES = 206 new SimpleAnnotationValueVisitor8<ImmutableList<AnnotationValue>, String>() { 207 @Override 208 public ImmutableList<AnnotationValue> visitArray( 209 List<? extends AnnotationValue> vals, String elementName) { 210 return ImmutableList.copyOf(vals); 211 } 212 213 @Override 214 protected ImmutableList<AnnotationValue> defaultAction(Object o, String elementName) { 215 throw new IllegalArgumentException(elementName + " is not an array: " + o); 216 } 217 }; 218 } 219