Home | History | Annotate | Download | only in processing
      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 
     61   public static TypeMirror getAnnotationTypeMirrorValue(AnnotationValue av) {
     62     return TYPE_MIRROR_VISITOR.visit(av);
     63   }
     64 
     65   public static TypeElement getAnnotationTypeMirrorValue(Element el) {
     66     return TYPE_ELEMENT_VISITOR.visit(el);
     67   }
     68 
     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 
     80   public static String getAnnotationStringValue(AnnotationValue av) {
     81     return STRING_VISITOR.visit(av);
     82   }
     83 
     84   public static int getAnnotationIntValue(AnnotationValue av) {
     85     return INT_VISITOR.visit(av);
     86   }
     87 
     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 
     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 
    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 
    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"})
    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 
    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 
    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 
    201   String getPackageOf(TypeElement typeElement) {
    202     PackageElement name = typeElement == null ? null : elements.getPackageOf(typeElement);
    203     return name == null ? null : name.toString();
    204   }
    205 
    206   String getBinaryName(TypeElement typeElement) {
    207     Name name = typeElement == null ? null : elements.getBinaryName(typeElement);
    208     return name == null ? null : name.toString();
    209   }
    210 
    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 
    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 
    246   public Element getPackageElement(String packageName) {
    247     return elements.getPackageElement(packageName);
    248   }
    249 
    250   public Element asElement(TypeMirror typeMirror) {
    251     return types.asElement(typeMirror);
    252   }
    253 
    254   public TypeElement getTypeElement(String className) {
    255     return elements.getTypeElement(className);
    256   }
    257 }
    258