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