• 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(AnnotationMirror annotationMirror,
70       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(Types types, Element element,
89       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(Element elem, Types types,
100       TypeElement typeElement) {
101     return getAnnotationMirror(types, elem, typeElement);
102   }
103 
104   private final Types types;
105   private final Elements elements;
106 
107   /**
108    * TypeMirror representing the Object class.
109    */
110   private final Predicate<TypeMirror> notObject;
111 
Helpers(ProcessingEnvironment environment)112   public Helpers(ProcessingEnvironment environment) {
113     this.elements = environment.getElementUtils();
114     this.types = environment.getTypeUtils();
115 
116     TypeMirror objectMirror = elements.getTypeElement(Object.class.getCanonicalName()).asType();
117     notObject = t -> !types.isSameType(t, objectMirror);
118   }
119 
getExplicitBounds(TypeParameterElement typeParam)120   List<TypeMirror> getExplicitBounds(TypeParameterElement typeParam) {
121     return newArrayList(Iterables.filter(typeParam.getBounds(), notObject));
122   }
123 
124   private final Equivalence<TypeMirror> typeMirrorEq = new Equivalence<TypeMirror>() {
125     @Override
126     protected boolean doEquivalent(TypeMirror a, TypeMirror b) {
127       return types.isSameType(a, b);
128     }
129 
130     @Override
131     protected int doHash(TypeMirror t) {
132       // We're not using the hash.
133       return 0;
134     }
135   };
136 
137   private final Equivalence<TypeParameterElement> typeEq = new Equivalence<TypeParameterElement>() {
138     @Override
139     @SuppressWarnings({"unchecked"})
140     protected boolean doEquivalent(TypeParameterElement arg0,
141         TypeParameterElement arg1) {
142       // Casts are necessary due to flaw in pairwise equivalence implementation.
143       return typeMirrorEq.pairwise().equivalent((List<TypeMirror>) arg0.getBounds(),
144           (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(List<? extends TypeParameterElement> l1,
156       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.pairwise().equivalent((List<TypeParameterElement>) l1,
162         (List<TypeParameterElement>) l2);
163   }
164 
getImplementedClassName(AnnotationMirror am)165   private TypeMirror getImplementedClassName(AnnotationMirror am) {
166     AnnotationValue className = Helpers.getAnnotationTypeMirrorValue(am, "className");
167     if (className == null) {
168       return null;
169     }
170     String classNameString = Helpers.getAnnotationStringValue(className);
171     if (classNameString == null) {
172       return null;
173     }
174     TypeElement impElement = elements.getTypeElement(classNameString.replace('$', '.'));
175     if (impElement == null) {
176       return null;
177     }
178     return impElement.asType();
179   }
180 
getImplementedClass(AnnotationMirror am)181   public TypeMirror getImplementedClass(AnnotationMirror am) {
182     if (am == null) {
183       return null;
184     }
185     // RobolectricWiringTest prefers className (if provided) to value, so we do the same here.
186     TypeMirror impType = getImplementedClassName(am);
187     if (impType != null) {
188       return impType;
189     }
190     AnnotationValue av = Helpers.getAnnotationTypeMirrorValue(am, "value");
191     if (av == null) {
192       return null;
193     }
194     TypeMirror type = Helpers.getAnnotationTypeMirrorValue(av);
195     if (type == null) {
196       return null;
197     }
198     return type;
199   }
200 
getPackageOf(TypeElement typeElement)201   String getPackageOf(TypeElement typeElement) {
202     PackageElement name = typeElement == null ? null : elements.getPackageOf(typeElement);
203     return name == null ? null : name.toString();
204   }
205 
getBinaryName(TypeElement typeElement)206   String getBinaryName(TypeElement typeElement) {
207     Name name = typeElement == null ? null : elements.getBinaryName(typeElement);
208     return name == null ? null : name.toString();
209   }
210 
appendParameterList(StringBuilder message, List<? extends TypeParameterElement> tpeList)211   public void appendParameterList(StringBuilder message,
212       List<? extends TypeParameterElement> tpeList) {
213     boolean first = true;
214     for (TypeParameterElement tpe : tpeList) {
215       if (first) {
216         first = false;
217       } else {
218         message.append(',');
219       }
220       message.append(tpe);
221       boolean iFirst = true;
222       for (TypeMirror bound : getExplicitBounds(tpe)) {
223         if (iFirst) {
224           message.append(" extends ");
225           iFirst = false;
226         } else {
227           message.append(',');
228         }
229         message.append(bound);
230       }
231     }
232   }
233 
findInterface(TypeElement shadowPickerType, Class<?> interfaceClass)234   TypeMirror findInterface(TypeElement shadowPickerType, Class<?> interfaceClass) {
235     TypeMirror shadowPickerMirror = elements
236         .getTypeElement(interfaceClass.getName())
237         .asType();
238     for (TypeMirror typeMirror : shadowPickerType.getInterfaces()) {
239       if (types.erasure(typeMirror).equals(types.erasure(shadowPickerMirror))) {
240         return typeMirror;
241       }
242     }
243     return null;
244   }
245 
getPackageElement(String packageName)246   public Element getPackageElement(String packageName) {
247     return elements.getPackageElement(packageName);
248   }
249 
asElement(TypeMirror typeMirror)250   public Element asElement(TypeMirror typeMirror) {
251     return types.asElement(typeMirror);
252   }
253 
getTypeElement(String className)254   public TypeElement getTypeElement(String className) {
255     return elements.getTypeElement(className);
256   }
257 }
258