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