• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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