• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.annotation.processing;
2 
3 import static com.google.common.collect.Lists.newArrayList;
4 
5 import com.google.common.base.Equivalence;
6 import com.google.common.base.Predicate;
7 import com.google.common.collect.Iterables;
8 import java.util.List;
9 import java.util.Map.Entry;
10 import javax.annotation.processing.ProcessingEnvironment;
11 import javax.lang.model.element.AnnotationMirror;
12 import javax.lang.model.element.AnnotationValue;
13 import javax.lang.model.element.AnnotationValueVisitor;
14 import javax.lang.model.element.Element;
15 import javax.lang.model.element.ElementVisitor;
16 import javax.lang.model.element.ExecutableElement;
17 import javax.lang.model.element.Name;
18 import javax.lang.model.element.PackageElement;
19 import javax.lang.model.element.TypeElement;
20 import javax.lang.model.element.TypeParameterElement;
21 import javax.lang.model.type.TypeMirror;
22 import javax.lang.model.util.Elements;
23 import javax.lang.model.util.SimpleAnnotationValueVisitor6;
24 import javax.lang.model.util.SimpleElementVisitor6;
25 import javax.lang.model.util.Types;
26 
27 public class Helpers {
28 
29   private static final AnnotationValueVisitor<TypeMirror, Void> TYPE_MIRROR_VISITOR =
30       new SimpleAnnotationValueVisitor6<TypeMirror, Void>() {
31         @Override
32         public TypeMirror visitType(TypeMirror t, Void arg) {
33           return t;
34         }
35       };
36 
37   private static final ElementVisitor<TypeElement, Void> TYPE_ELEMENT_VISITOR =
38       new SimpleElementVisitor6<TypeElement, Void>() {
39         @Override
40         public TypeElement visitType(TypeElement e, Void p) {
41           return e;
42         }
43       };
44 
45   private static final AnnotationValueVisitor<String, Void> STRING_VISITOR =
46       new SimpleAnnotationValueVisitor6<String, Void>() {
47         @Override
48         public String visitString(String s, Void arg) {
49           return s;
50         }
51       };
52 
53   private static final AnnotationValueVisitor<Integer, Void> INT_VISITOR =
54       new SimpleAnnotationValueVisitor6<Integer, Void>() {
55         @Override
56         public Integer visitInt(int i, Void aVoid) {
57           return i;
58         }
59       };
60 
getAnnotationTypeMirrorValue(AnnotationValue av)61   public static TypeMirror getAnnotationTypeMirrorValue(AnnotationValue av) {
62     return TYPE_MIRROR_VISITOR.visit(av);
63   }
64 
getAnnotationTypeMirrorValue(Element el)65   public static TypeElement getAnnotationTypeMirrorValue(Element el) {
66     return TYPE_ELEMENT_VISITOR.visit(el);
67   }
68 
getAnnotationTypeMirrorValue( AnnotationMirror annotationMirror, String key)69   public static AnnotationValue getAnnotationTypeMirrorValue(
70       AnnotationMirror annotationMirror, String key) {
71     for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
72         annotationMirror.getElementValues().entrySet()) {
73       if (entry.getKey().getSimpleName().contentEquals(key)) {
74         return entry.getValue();
75       }
76     }
77     return null;
78   }
79 
getAnnotationStringValue(AnnotationValue av)80   public static String getAnnotationStringValue(AnnotationValue av) {
81     return STRING_VISITOR.visit(av);
82   }
83 
getAnnotationIntValue(AnnotationValue av)84   public static int getAnnotationIntValue(AnnotationValue av) {
85     return INT_VISITOR.visit(av);
86   }
87 
getAnnotationMirror( Types types, Element element, TypeElement annotation)88   public static AnnotationMirror getAnnotationMirror(
89       Types types, Element element, TypeElement annotation) {
90     TypeMirror expectedType = annotation.asType();
91     for (AnnotationMirror m : element.getAnnotationMirrors()) {
92       if (types.isSameType(expectedType, m.getAnnotationType())) {
93         return m;
94       }
95     }
96     return null;
97   }
98 
getImplementsMirror( Element elem, Types types, TypeElement typeElement)99   public static AnnotationMirror getImplementsMirror(
100       Element elem, Types types, TypeElement typeElement) {
101     return getAnnotationMirror(types, elem, typeElement);
102   }
103 
104   private final Types types;
105   private final Elements elements;
106 
107   /** TypeMirror representing the Object class. */
108   private final Predicate<TypeMirror> notObject;
109 
Helpers(ProcessingEnvironment environment)110   public Helpers(ProcessingEnvironment environment) {
111     this.elements = environment.getElementUtils();
112     this.types = environment.getTypeUtils();
113 
114     TypeMirror objectMirror = elements.getTypeElement(Object.class.getCanonicalName()).asType();
115     notObject = t -> !types.isSameType(t, objectMirror);
116   }
117 
getExplicitBounds(TypeParameterElement typeParam)118   List<TypeMirror> getExplicitBounds(TypeParameterElement typeParam) {
119     return newArrayList(Iterables.filter(typeParam.getBounds(), notObject));
120   }
121 
122   private final Equivalence<TypeMirror> typeMirrorEq =
123       new Equivalence<TypeMirror>() {
124         @Override
125         protected boolean doEquivalent(TypeMirror a, TypeMirror b) {
126           return types.isSameType(a, b);
127         }
128 
129         @Override
130         protected int doHash(TypeMirror t) {
131           // We're not using the hash.
132           return 0;
133         }
134       };
135 
136   private final Equivalence<TypeParameterElement> typeEq =
137       new Equivalence<TypeParameterElement>() {
138         @Override
139         @SuppressWarnings({"unchecked"})
140         protected boolean doEquivalent(TypeParameterElement arg0, TypeParameterElement arg1) {
141           // Casts are necessary due to flaw in pairwise equivalence implementation.
142           return typeMirrorEq
143               .pairwise()
144               .equivalent((List<TypeMirror>) arg0.getBounds(), (List<TypeMirror>) arg1.getBounds());
145         }
146 
147         @Override
148         protected int doHash(TypeParameterElement arg0) {
149           // We don't use the hash code.
150           return 0;
151         }
152       };
153 
154   @SuppressWarnings({"unchecked"})
isSameParameterList( List<? extends TypeParameterElement> l1, List<? extends TypeParameterElement> l2)155   public boolean isSameParameterList(
156       List<? extends TypeParameterElement> l1, List<? extends TypeParameterElement> l2) {
157     // Cast is necessary because of a flaw in the API design of "PairwiseEquivalent",
158     // a flaw that is even acknowledged in the source.
159     // Our casts are safe because we're not trying to add elements to the list
160     // and therefore can't violate the constraint.
161     return typeEq
162         .pairwise()
163         .equivalent((List<TypeParameterElement>) l1, (List<TypeParameterElement>) l2);
164   }
165 
getImplementedClassName(AnnotationMirror am)166   private TypeMirror getImplementedClassName(AnnotationMirror am) {
167     AnnotationValue className = Helpers.getAnnotationTypeMirrorValue(am, "className");
168     if (className == null) {
169       return null;
170     }
171     String classNameString = Helpers.getAnnotationStringValue(className);
172     if (classNameString == null) {
173       return null;
174     }
175     TypeElement impElement = elements.getTypeElement(classNameString.replace('$', '.'));
176     if (impElement == null) {
177       return null;
178     }
179     return impElement.asType();
180   }
181 
getImplementedClass(AnnotationMirror am)182   public TypeMirror getImplementedClass(AnnotationMirror am) {
183     if (am == null) {
184       return null;
185     }
186     // RobolectricWiringTest prefers className (if provided) to value, so we do the same here.
187     TypeMirror impType = getImplementedClassName(am);
188     if (impType != null) {
189       return impType;
190     }
191     AnnotationValue av = Helpers.getAnnotationTypeMirrorValue(am, "value");
192     if (av == null) {
193       return null;
194     }
195     TypeMirror type = Helpers.getAnnotationTypeMirrorValue(av);
196     if (type == null) {
197       return null;
198     }
199     return type;
200   }
201 
getPackageOf(TypeElement typeElement)202   String getPackageOf(TypeElement typeElement) {
203     PackageElement name = typeElement == null ? null : elements.getPackageOf(typeElement);
204     return name == null ? null : name.toString();
205   }
206 
getBinaryName(TypeElement typeElement)207   String getBinaryName(TypeElement typeElement) {
208     Name name = typeElement == null ? null : elements.getBinaryName(typeElement);
209     return name == null ? null : name.toString();
210   }
211 
appendParameterList( StringBuilder message, List<? extends TypeParameterElement> tpeList)212   public void appendParameterList(
213       StringBuilder message, List<? extends TypeParameterElement> tpeList) {
214     boolean first = true;
215     if (tpeList == null || tpeList.isEmpty()) {
216       return;
217     }
218     message.append("<");
219     for (TypeParameterElement tpe : tpeList) {
220       if (first) {
221         first = false;
222       } else {
223         message.append(", ");
224       }
225       message.append(tpe);
226       boolean iFirst = true;
227       for (TypeMirror bound : getExplicitBounds(tpe)) {
228         if (iFirst) {
229           message.append(" extends ");
230           iFirst = false;
231         } else {
232           message.append(',');
233         }
234         message.append(bound);
235       }
236     }
237     message.append(">");
238   }
239 
findInterface(TypeElement shadowPickerType, Class<?> interfaceClass)240   TypeMirror findInterface(TypeElement shadowPickerType, Class<?> interfaceClass) {
241     TypeMirror shadowPickerMirror = elements.getTypeElement(interfaceClass.getName()).asType();
242     for (TypeMirror typeMirror : shadowPickerType.getInterfaces()) {
243       if (types.isSameType(types.erasure(typeMirror), types.erasure(shadowPickerMirror))) {
244         return typeMirror;
245       }
246     }
247     return null;
248   }
249 
getPackageElement(String packageName)250   public Element getPackageElement(String packageName) {
251     return elements.getPackageElement(packageName);
252   }
253 
asElement(TypeMirror typeMirror)254   public Element asElement(TypeMirror typeMirror) {
255     return types.asElement(typeMirror);
256   }
257 
getTypeElement(String className)258   public TypeElement getTypeElement(String className) {
259     return elements.getTypeElement(className);
260   }
261 }
262